Make WordPress Core

Changeset 55971


Ignore:
Timestamp:
06/21/2023 09:45:52 PM (3 years ago)
Author:
joedolson
Message:

Administration: Set accessible state for list table headers.

Implement aria-sort and change icon states to indicate current sort for list tables. Allow screen reader users to get context about the current sort and allow sighted users to know how the table is currently sorted.

Props afercia, rianrietveld, joedolson, alexstine, johnjamesjacoby.
Fixes #32170.

Location:
trunk/src/wp-admin
Files:
11 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/wp-admin/css/list-tables.css

    r55954 r55971  
    462462}
    463463
     464.sorting-indicators {
     465        display: grid;
     466}
     467
    464468.sorting-indicator {
    465469        display: block;
    466         visibility: hidden;
    467470        width: 10px;
    468471        height: 4px;
    469         margin-top: 8px;
     472        margin-top: 4px;
    470473        margin-left: 7px;
    471474}
    472475
    473476.sorting-indicator:before {
    474         content: "\f142";
    475477        font: normal 20px/1 dashicons;
    476478        speak: never;
     
    479481        top: -4px;
    480482        left: -8px;
    481         color: #3c434a;
    482483        line-height: 0.5;
    483484        position: relative;
     
    486487        -moz-osx-font-smoothing: grayscale;
    487488        text-decoration: none !important;
    488         color: #3c434a;
    489 }
    490 
    491 .column-comments .sorting-indicator:before {
    492         top: 0;
    493         left: -10px;
    494 }
    495 
    496 th.sorted.asc .sorting-indicator:before,
    497 th.desc:hover span.sorting-indicator:before,
    498 th.desc a:focus span.sorting-indicator:before {
     489        color: #a7aaad;
     490}
     491
     492.sorting-indicator.asc:before {
    499493        content: "\f142";
    500494}
    501495
    502 th.sorted.desc .sorting-indicator:before,
    503 th.asc:hover span.sorting-indicator:before,
    504 th.asc a:focus span.sorting-indicator:before {
     496.sorting-indicator.desc:before {
    505497        content: "\f140";
     498}
     499
     500th.sorted.desc .sorting-indicator.desc:before {
     501        color: #1d2327;
     502}
     503
     504th.sorted.asc .sorting-indicator.asc:before {
     505        color: #1d2327;
     506}
     507
     508th.sorted.asc a:focus .sorting-indicator.asc:before,
     509th.sorted.asc:hover .sorting-indicator.asc:before,
     510th.sorted.desc a:focus .sorting-indicator.desc:before,
     511th.sorted.desc:hover .sorting-indicator.desc:before {
     512        color: #a7aaad;
     513}
     514
     515th.sorted.asc a:focus .sorting-indicator.desc:before,
     516th.sorted.asc:hover .sorting-indicator.desc:before,
     517th.sorted.desc a:focus .sorting-indicator.asc:before,
     518th.sorted.desc:hover .sorting-indicator.asc:before {
     519        color: #1d2327;
    506520}
    507521
     
    614628}
    615629
    616 .fixed .column-comments .sorting-indicator {
    617         margin-top: 3px;
    618 }
    619 
    620630#menu-locations-wrap .widefat {
    621631        width: 60%;
     
    643653        float: left;
    644654        cursor: pointer;
    645 }
    646 
    647 th.sorted .sorting-indicator,
    648 th.desc:hover span.sorting-indicator,
    649 th.desc a:focus span.sorting-indicator,
    650 th.asc:hover span.sorting-indicator,
    651 th.asc a:focus span.sorting-indicator {
    652         visibility: visible;
    653655}
    654656
  • trunk/src/wp-admin/includes/class-wp-comments-list-table.php

    r55954 r55971  
    541541        protected function get_sortable_columns() {
    542542                return array(
    543                         'author'   => 'comment_author',
    544                         'response' => 'comment_post_ID',
     543                        'author'   => array( 'comment_author', false, __( 'Author' ), __( 'Table ordered by Comment Author.' ) ),
     544                        'response' => array( 'comment_post_ID', false, _x( 'In Response To', 'column name' ), __( 'Table ordered by Post Replied To.' ) ),
    545545                        'date'     => 'comment_date',
    546546                );
     
    581581                ?>
    582582<table class="wp-list-table <?php echo implode( ' ', $this->get_table_classes() ); ?>">
     583                <?php
     584                if ( ! isset( $_GET['orderby'] ) ) {
     585                        // In the initial view, Comments are ordered by comment's date but there's no column for that.
     586                        echo '<caption class="screen-reader-text">' . __( 'Ordered by Comment Date, descending.' ) . '</p>';
     587                } else {
     588                        $this->print_table_description();
     589                }
     590                ?>
    583591        <thead>
    584592        <tr>
  • trunk/src/wp-admin/includes/class-wp-links-list-table.php

    r55954 r55971  
    144144        protected function get_sortable_columns() {
    145145                return array(
    146                         'name'    => 'name',
    147                         'url'     => 'url',
    148                         'visible' => 'visible',
    149                         'rating'  => 'rating',
     146                        'name'    => array( 'name', false, _x( 'Name', 'link name' ), __( 'Table ordered by Name.' ), 'asc' ),
     147                        'url'     => array( 'url', false, __( 'URL' ), __( 'Table ordered by URL.' ) ),
     148                        'visible' => array( 'visible', false, __( 'Visible' ), __( 'Table ordered by Visibility.' ) ),
     149                        'rating'  => array( 'rating', false, __( 'Rating' ), __( 'Table ordered by Rating.' ) ),
    150150                );
    151151        }
  • trunk/src/wp-admin/includes/class-wp-list-table.php

    r55954 r55971  
    11101110         * The format is:
    11111111         * - `'internal-name' => 'orderby'`
     1112         * - `'internal-name' => array( 'orderby', bool, 'abbr', 'orderby-text', 'initially-sorted-column-order' )` -
    11121113         * - `'internal-name' => array( 'orderby', 'asc' )` - The second element sets the initial sorting order.
    11131114         * - `'internal-name' => array( 'orderby', true )`  - The second element makes the initial order descending.
    11141115         *
    1115          * @since 3.1.0
     1116         * In the second format, passing true as second parameter will make the initial
     1117         * sorting order be descending. Following parameters add a short column name to
     1118         * be used as 'abbr' attribute, a translatable string for the current sorting
     1119         * and the initial order for the initial sorted column, 'asc' or 'desc' (default: false).
     1120         *
     1121         * @since 3.1.0
     1122         * @since 6.3.0 Added 'abbr', 'orderby-text' and 'initially-sorted-column-order'.
    11161123         *
    11171124         * @return array
     
    12541261
    12551262                        $data = (array) $data;
     1263                        // Descending initial sorting.
    12561264                        if ( ! isset( $data[1] ) ) {
    12571265                                $data[1] = false;
     1266                        }
     1267                        // Current sorting translatable string.
     1268                        if ( ! isset( $data[2] ) ) {
     1269                                $data[2] = '';
     1270                        }
     1271                        // Initial view sorted column and asc/desc order, default: false.
     1272                        if ( ! isset( $data[3] ) ) {
     1273                                $data[3] = false;
     1274                        }
     1275                        // Initial order for the initial sorted column, default: false.
     1276                        if ( ! isset( $data[4] ) ) {
     1277                                $data[4] = false;
    12581278                        }
    12591279
     
    12931313                $current_url = remove_query_arg( 'paged', $current_url );
    12941314
     1315                // When users click on a column header to sort by other columns.
    12951316                if ( isset( $_GET['orderby'] ) ) {
    12961317                        $current_orderby = $_GET['orderby'];
     1318                        // In the initial view there's no orderby parameter.
    12971319                } else {
    12981320                        $current_orderby = '';
    12991321                }
    13001322
    1301                 if ( isset( $_GET['order'] ) && 'desc' === $_GET['order'] ) {
     1323                // Not in the initial view and descending order.
     1324                if ( isset( $_GET['order'] ) && 'desc' == $_GET['order'] ) {
    13021325                        $current_order = 'desc';
    13031326                } else {
     1327                        // The initial view is not always 'asc' we'll take care of this below.
    13041328                        $current_order = 'asc';
    13051329                }
     
    13181342
    13191343                foreach ( $columns as $column_key => $column_display_name ) {
    1320                         $class = array( 'manage-column', "column-$column_key" );
     1344                        $class          = array( 'manage-column', "column-$column_key" );
     1345                        $aria_sort_attr = '';
     1346                        $abbr_attr      = '';
     1347                        $order_text     = '';
    13211348
    13221349                        if ( in_array( $column_key, $hidden, true ) ) {
     
    13351362
    13361363                        if ( isset( $sortable[ $column_key ] ) ) {
    1337                                 list( $orderby, $desc_first ) = $sortable[ $column_key ];
    1338 
     1364                                list( $orderby, $desc_first, $abbr, $orderby_text, $initial_order ) = $sortable[ $column_key ];
     1365
     1366                                /*
     1367                                 * We're in the initial view and there's no $_GET['orderby'] then check if the
     1368                                 * initial sorting information is set in the sortable columns and use that.
     1369                                 */
     1370                                if ( '' === $current_orderby && $initial_order ) {
     1371                                        // Use the initially sorted column $orderby as current orderby.
     1372                                        $current_orderby = $orderby;
     1373                                        // Use the initially sorted column asc/desc order as initial order.
     1374                                        $current_order = $initial_order;
     1375                                }
     1376
     1377                                /*
     1378                                 * True in the initial view when an initial orderby is set via get_sortable_columns()
     1379                                 * and true in the sorted views when the actual $_GET['orderby'] is equal to $orderby.
     1380                                 */
    13391381                                if ( $current_orderby === $orderby ) {
    1340                                         $order = 'asc' === $current_order ? 'desc' : 'asc';
    1341 
     1382                                        // The sorted column. The `aria-sort` attribute must be set only on the sorted column.
     1383                                        if ( 'asc' == $current_order ) {
     1384                                                $order          = 'desc';
     1385                                                $aria_sort_attr = ' aria-sort="ascending"';
     1386                                        } else {
     1387                                                $order          = 'asc';
     1388                                                $aria_sort_attr = ' aria-sort="descending"';
     1389                                        }
    13421390                                        $class[] = 'sorted';
    13431391                                        $class[] = $current_order;
    13441392                                } else {
     1393                                        // The other sortable columns.
    13451394                                        $order = strtolower( $desc_first );
    13461395
     
    13491398                                        }
    13501399
    1351                                         $class[] = 'sortable';
    1352                                         $class[] = 'desc' === $order ? 'asc' : 'desc';
     1400                                        $class[]    = 'sortable';
     1401                                        $class[]    = 'desc' === $order ? 'asc' : 'desc';
     1402                                        $order_text = 'asc' === $order ? __( 'Sort ascending.' ) : __( 'Sort descending.' );
    13531403                                }
    1354 
    1355                                 $column_display_name = sprintf(
    1356                                         '<a href="%s"><span>%s</span><span class="sorting-indicator"></span></a>',
    1357                                         esc_url( add_query_arg( compact( 'orderby', 'order' ), $current_url ) ),
    1358                                         $column_display_name
    1359                                 );
     1404                                if ( '' !== $order_text ) {
     1405                                        $order_text = ' <span class="screen-reader-text">' . $order_text . '</span>';
     1406                                }
     1407
     1408                                // Print an 'abbr' attribute if a value is provided via get_sortable_columns().
     1409                                $abbr_attr           = $abbr ? ' abbr="' . esc_attr( $abbr ) . '"' : '';
     1410                                $column_display_name = '<a href="' . esc_url( add_query_arg( compact( 'orderby', 'order' ), $current_url ) ) . '"><span>' . $column_display_name . '</span><span class="sorting-indicators"><span class="sorting-indicator asc" aria-hidden="true"></span><span class="sorting-indicator desc" aria-hidden="true"></span>' . $order_text . '</a>';
    13601411                        }
    13611412
     
    13681419                        }
    13691420
    1370                         echo "<$tag $scope $id $class>$column_display_name</$tag>";
     1421                        echo "<$tag $scope $id $class $aria_sort_attr $abbr_attr>$column_display_name</$tag>";
     1422                }
     1423        }
     1424
     1425        /**
     1426         * Print a table description with information about current sorting and order.
     1427         *
     1428         * For the table initial view, information about initial orderby and order
     1429         * should be provided via get_sortable_columns().
     1430         *
     1431         * @since 4.3.0
     1432         * @access public
     1433         */
     1434        public function print_table_description() {
     1435                list( $columns, $hidden, $sortable ) = $this->get_column_info();
     1436
     1437                if ( empty( $sortable ) ) {
     1438                        return;
     1439                }
     1440
     1441                // When users click on a column header to sort by other columns.
     1442                if ( isset( $_GET['orderby'] ) ) {
     1443                        $current_orderby = $_GET['orderby'];
     1444                        // In the initial view there's no orderby parameter.
     1445                } else {
     1446                        $current_orderby = '';
     1447                }
     1448
     1449                // Not in the initial view and descending order.
     1450                if ( isset( $_GET['order'] ) && 'desc' == $_GET['order'] ) {
     1451                        $current_order = 'desc';
     1452                } else {
     1453                        // The initial view is not always 'asc' we'll take care of this below.
     1454                        $current_order = 'asc';
     1455                }
     1456
     1457                foreach ( array_keys( $columns ) as $column_key ) {
     1458
     1459                        if ( isset( $sortable[ $column_key ] ) ) {
     1460
     1461                                list( $orderby, $desc_first, $abbr, $orderby_text, $initial_order ) = $sortable[ $column_key ];
     1462
     1463                                if ( ! is_string( $orderby_text ) || '' === $orderby_text ) {
     1464                                        return;
     1465                                }
     1466                                /*
     1467                                 * We're in the initial view and there's no $_GET['orderby'] then check if the
     1468                                 * initial sorting information is set in the sortable columns and use that.
     1469                                 */
     1470                                if ( '' === $current_orderby && $initial_order ) {
     1471                                        // Use the initially sorted column $orderby as current orderby.
     1472                                        $current_orderby = $orderby;
     1473                                        // Use the initially sorted column asc/desc order as initial order.
     1474                                        $current_order = $initial_order;
     1475                                }
     1476
     1477                                /*
     1478                                 * True in the initial view when an initial orderby is set via get_sortable_columns()
     1479                                 * and true in the sorted views when the actual $_GET['orderby'] is equal to $orderby.
     1480                                 */
     1481                                if ( $current_orderby == $orderby ) {
     1482                                        $order_text = 'asc' === $current_order ? __( 'Ascending.' ) : __( 'Descending.' );
     1483                                        echo '<caption  class="screen-reader-text">' . $orderby_text . ' ' . $order_text . '</p>';
     1484
     1485                                        return;
     1486                                }
     1487                        }
    13711488                }
    13721489        }
     
    13851502                ?>
    13861503<table class="wp-list-table <?php echo implode( ' ', $this->get_table_classes() ); ?>">
     1504                <?php $this->print_table_description(); ?>
    13871505        <thead>
    13881506        <tr>
  • trunk/src/wp-admin/includes/class-wp-media-list-table.php

    r55954 r55971  
    390390        protected function get_sortable_columns() {
    391391                return array(
    392                         'title'    => 'title',
    393                         'author'   => 'author',
    394                         'parent'   => 'parent',
    395                         'comments' => 'comment_count',
    396                         'date'     => array( 'date', true ),
     392                        'title'    => array( 'title', false, _x( 'File', 'column name' ), __( 'Table ordered by File Name.' ) ),
     393                        'author'   => array( 'author', false, __( 'Author' ), __( 'Table ordered by Author.' ) ),
     394                        'parent'   => array( 'parent', false, _x( 'Uploaded to', 'column name' ), __( 'Table ordered by Uploaded To.' ) ),
     395                        'comments' => array( 'comment_count', __( 'Comments' ), false, __( 'Table ordered by Comments.' ) ),
     396                        'date'     => array( 'date', true, __( 'Date' ), __( 'Table ordered by Date.' ), 'desc' ),
    397397                );
    398398        }
  • trunk/src/wp-admin/includes/class-wp-ms-sites-list-table.php

    r55954 r55971  
    390390         */
    391391        protected function get_sortable_columns() {
     392
     393                if ( is_subdomain_install() ) {
     394                        $abbr = __( 'Domain' );
     395                        $blogname_orderby_text = __( 'Table ordered by Site Domain Name.' );
     396                } else {
     397                        $abbr = __( 'Path' );
     398                        $blogname_orderby_text = __( 'Table ordered by Site Path.' );
     399                }
     400
    392401                return array(
    393                         'blogname'    => 'blogname',
    394                         'lastupdated' => 'lastupdated',
    395                         'registered'  => 'blog_id',
     402                        'blogname'    => array( 'blogname', false, $abbr, $blogname_orderby_text ),
     403                        'lastupdated' => array( 'lastupdated', true, __( 'Last Updated' ), __( 'Table ordered by Last Updated.' ) ),
     404                        'registered'  => array( 'blog_id', true, _x( 'Registered', 'site' ), __( 'Table ordered by Site Registered Date.' ), 'desc' ),
    396405                );
    397406        }
  • trunk/src/wp-admin/includes/class-wp-ms-themes-list-table.php

    r55954 r55971  
    344344        protected function get_sortable_columns() {
    345345                return array(
    346                         'name' => 'name',
     346                        'name' => array( 'name', false, __( 'Theme' ), __( 'Table ordered by Theme Name.' ), 'asc' ),
    347347                );
    348348        }
  • trunk/src/wp-admin/includes/class-wp-ms-users-list-table.php

    r55954 r55971  
    213213        protected function get_sortable_columns() {
    214214                return array(
    215                         'username'   => 'login',
    216                         'name'       => 'name',
    217                         'email'      => 'email',
    218                         'registered' => 'id',
     215                        'username'   => array( 'login', false, __( 'Username' ), __( 'Table ordered by Username.' ), 'asc' ),
     216                        'name'       => array( 'name', false, __( 'Name' ), __( 'Table ordered by Name.' ) ),
     217                        'email'      => array( 'email', false, __( 'E-mail' ), __( 'Table ordered by E-mail.' ) ),
     218                        'registered' => array( 'id', false, _x( 'Registered', 'user' ), __( 'Table ordered by User Registered Date.' ) ),
    219219                );
    220220        }
  • trunk/src/wp-admin/includes/class-wp-posts-list-table.php

    r55954 r55971  
    761761         */
    762762        protected function get_sortable_columns() {
    763                 return array(
    764                         'title'    => 'title',
    765                         'parent'   => 'parent',
    766                         'comments' => 'comment_count',
    767                         'date'     => array( 'date', true ),
    768                 );
     763
     764                $post_type = $this->screen->post_type;
     765
     766                if ( 'page' === $post_type ) {
     767                        $title_orderby_text = isset( $_GET['orderby'] ) ? __( 'Table ordered by Title.' ) : __( 'Table ordered by Hierarchical Menu Order and Title.' );
     768                        $sortables = array(
     769                                'title'    => array( 'title', false, __( 'Title' ), $title_orderby_text, 'asc' ),
     770                                'parent'   => array( 'parent', false ),
     771                                'comments' => array( 'comment_count', false, __( 'Comments' ), __( 'Table ordered by Comments.' ) ),
     772                                'date'     => array( 'date', true, __( 'Date' ), __( 'Table ordered by Date.' ) ),
     773                        );
     774                } else {
     775                        $sortables = array(
     776                                'title'    => array( 'title', false, __( 'Title' ), __( 'Table ordered by Title.' ) ),
     777                                'parent'   => array( 'parent', false ),
     778                                'comments' => array( 'comment_count', false, __( 'Comments' ), __( 'Table ordered by Comments.' ) ),
     779                                'date'     => array( 'date', true, __( 'Date' ), __( 'Table ordered by Date.' ), 'desc' ),
     780                        );
     781                }
     782                // Custom Post Types: there's a filter for that, see get_column_info().
     783
     784                return $sortables;
    769785        }
    770786
  • trunk/src/wp-admin/includes/class-wp-terms-list-table.php

    r55954 r55971  
    209209         */
    210210        protected function get_sortable_columns() {
     211                $taxonomy = $this->screen->taxonomy;
     212
     213                if ( ! isset( $_GET['orderby'] ) && is_taxonomy_hierarchical( $taxonomy ) ) {
     214                        $name_orderby_text = __( 'Table ordered hierarchically.' );
     215                } else {
     216                        $name_orderby_text = __( 'Table ordered by Name.' );
     217                }
     218
    211219                return array(
    212                         'name'        => 'name',
    213                         'description' => 'description',
    214                         'slug'        => 'slug',
    215                         'posts'       => 'count',
    216                         'links'       => 'count',
     220                        'name'        => array( 'name', false, _x( 'Name', 'term name' ), $name_orderby_text, 'asc' ),
     221                        'description' => array( 'description', false, __( 'Description' ), __( 'Table ordered by Description.' ) ),
     222                        'slug'        => array( 'slug', false, __( 'Slug' ), __( 'Table ordered by Slug.' ) ),
     223                        'posts'       => array( 'count', false, _x( 'Count', 'Number/count of items' ), __( 'Table ordered by Posts Count.' ) ),
     224                        'links'       => array( 'count', false, __( 'Links' ), __( 'Table ordered by Links.' ) ),
    217225                );
    218226        }
  • trunk/src/wp-admin/includes/class-wp-users-list-table.php

    r55954 r55971  
    394394        protected function get_sortable_columns() {
    395395                $columns = array(
    396                         'username' => 'login',
    397                         'email'    => 'email',
     396                        'username' => array( 'login', false, __( 'Username' ), __( 'Table ordered by Username.' ), 'asc' ),
     397                        'email'    => array( 'email', false, __( 'E-mail' ), __( 'Table ordered by E-mail.' ) ),
    398398                );
    399399
Note: See TracChangeset for help on using the changeset viewer.