Make WordPress Core

Changeset 55971


Ignore:
Timestamp:
06/21/2023 09:45:52 PM (11 months 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.