A11Y: improve topic list table markup for screenreaders (#27808)

Co-authored-by: Jarek Radosz <jradosz@gmail.com>
This commit is contained in:
Kris 2024-07-10 13:14:36 -04:00 committed by GitHub
parent aedff155bd
commit 0e3ed7ea2a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 40 additions and 35 deletions

View File

@ -1,3 +1,5 @@
<caption class="sr-only">{{i18n "sr_topic_list_caption"}}</caption>
<thead class="topic-list-header">
{{raw
"topic-list-header"

View File

@ -101,6 +101,7 @@ export default class TopicList extends Component {
(if this.bulkSelectEnabled "sticky-header")
}}
>
<caption class="sr-only">{{i18n "sr_topic_list_caption"}}</caption>
<thead class="topic-list-header">
<TopicListHeader
@canBulkSelect={{@canBulkSelect}}

View File

@ -66,6 +66,7 @@ export default class TopicEntrance extends Component {
<template>
<DMenu
@title={{@title}}
@ariaLabel={{@title}}
@placement="center"
@autofocus={{true}}

View File

@ -83,9 +83,6 @@ export default class TopicListHeaderColumn extends Component {
{{(if @sortable (modifier on "keydown" this.onKeyDown))}}
data-sort-order={{@order}}
scope="col"
tabindex={{if @sortable "0"}}
role={{if @sortable "button"}}
aria-pressed={{this.isSorting}}
aria-sort={{this.ariaSort}}
class={{concatClass
"topic-list-data"
@ -144,7 +141,14 @@ export default class TopicListHeaderColumn extends Component {
@changeNewListSubset={{@changeNewListSubset}}
/>
{{else}}
<span>{{this.localizedName}}</span>
<span
class={{if @screenreaderOnly "sr-only"}}
tabindex={{if @sortable "0"}}
role={{if @sortable "button"}}
aria-pressed={{this.isSorting}}
>
{{this.localizedName}}
</span>
{{/if}}
{{/unless}}

View File

@ -50,6 +50,8 @@ const TopicListHeader = <template>
@activeOrder={{@order}}
@changeSort={{@changeSort}}
@ascending={{@ascending}}
@name="posters"
@screenreaderOnly={{true}}
aria-label={{i18n "category.sort_options.posters"}}
/>
{{/if}}
@ -62,7 +64,6 @@ const TopicListHeader = <template>
@changeSort={{@changeSort}}
@ascending={{@ascending}}
@name="replies"
aria-label={{i18n "sr_replies"}}
/>
{{#if @showLikes}}
@ -74,7 +75,6 @@ const TopicListHeader = <template>
@changeSort={{@changeSort}}
@ascending={{@ascending}}
@name="likes"
aria-label={{i18n "sr_likes"}}
/>
{{/if}}
@ -87,7 +87,6 @@ const TopicListHeader = <template>
@changeSort={{@changeSort}}
@ascending={{@ascending}}
@name="likes"
aria-label={{i18n "sr_op_likes"}}
/>
{{/if}}
@ -99,7 +98,6 @@ const TopicListHeader = <template>
@changeSort={{@changeSort}}
@ascending={{@ascending}}
@name="views"
aria-label={{i18n "sr_views"}}
/>
<TopicListHeaderColumn
@ -110,7 +108,6 @@ const TopicListHeader = <template>
@changeSort={{@changeSort}}
@ascending={{@ascending}}
@name="activity"
aria-label={{i18n "sr_activity"}}
/>
<PluginOutlet @name="topic-list-header-after" />

View File

@ -1,5 +1,5 @@
<{{view.tagName}} class='num posts-map posts {{view.likesHeat}} topic-list-data' title='{{view.title}}'>
<button class="btn-link posts-map badge-posts {{view.likesHeat}}" aria-label="{{view.title}}">
<{{view.tagName}} class='num posts-map posts {{view.likesHeat}} topic-list-data'>
<button class="btn-link posts-map badge-posts {{view.likesHeat}}" title="{{view.title}}" aria-label="{{view.title}}">
{{raw-plugin-outlet name="topic-list-before-reply-count"}}
{{number topic.replyCount noTitle="true"}}
</button>

View File

@ -1,4 +1,4 @@
<th data-sort-order='{{order}}' class='{{view.className}} topic-list-data' scope="col" {{#if ariaLabel}}aria-label='{{ariaLabel}}'{{/if}} {{#if sortable}}tabindex="0" role="button" aria-pressed='{{view.ariaPressed}}' {{#if view.ariaSort}}aria-sort='{{view.ariaSort}}'{{/if}} {{/if}}>
<th data-sort-order='{{order}}' class='{{view.className}} topic-list-data' scope="col" {{#if view.ariaSort}}aria-sort='{{view.ariaSort}}'{{/if}}>
{{~#if canBulkSelect}}
{{~#if showBulkToggle}}
{{~#if experimentalTopicBulkActionsEnabled }}
@ -25,7 +25,7 @@
{{~#if view.showTopicsAndRepliesToggle}}
{{raw "list/new-list-header-controls" current=newListSubset newRepliesCount=newRepliesCount newTopicsCount=newTopicsCount}}
{{else}}
<span>{{view.localizedName}}</span>
<span {{#if view.screenreaderOnly}}class="sr-only"{{/if}} {{#if sortable}}tabindex="0" role="button" aria-pressed='{{view.ariaPressed}}'{{/if}}>{{view.localizedName}}</span>
{{/if ~}}
{{/unless ~}}
{{~#if view.isSorting}}

View File

@ -13,15 +13,15 @@
{{raw "topic-list-header-column" order='default' name=listTitle bulkSelectEnabled=bulkSelectEnabled showBulkToggle=toggleInTitle canBulkSelect=canBulkSelect canDoBulkActions=canDoBulkActions showTopicsAndRepliesToggle=showTopicsAndRepliesToggle newListSubset=newListSubset newRepliesCount=newRepliesCount newTopicsCount=newTopicsCount experimentalTopicBulkActionsEnabled=experimentalTopicBulkActionsEnabled bulkSelectHelper=bulkSelectHelper }}
{{raw-plugin-outlet name="topic-list-header-after-main-link"}}
{{#if showPosters}}
{{raw "topic-list-header-column" order='posters' ariaLabel=(i18n "category.sort_options.posters")}}
{{raw "topic-list-header-column" name='posters' screenreaderOnly='true'}}
{{/if}}
{{raw "topic-list-header-column" sortable=sortable number='true' order='posts' name='replies' ariaLabel=(i18n "sr_replies")}}
{{raw "topic-list-header-column" sortable=sortable number='true' order='posts' name='replies'}}
{{#if showLikes}}
{{raw "topic-list-header-column" sortable=sortable number='true' order='likes' name='likes' ariaLabel=(i18n "sr_likes")}}
{{raw "topic-list-header-column" sortable=sortable number='true' order='likes' name='likes'}}
{{/if}}
{{#if showOpLikes}}
{{raw "topic-list-header-column" sortable=sortable number='true' order='op_likes' name='likes' ariaLabel=(i18n "sr_op_likes")}}
{{raw "topic-list-header-column" sortable=sortable number='true' order='op_likes' name='likes'}}
{{/if}}
{{raw "topic-list-header-column" sortable=sortable number='true' order='views' name='views' ariaLabel=(i18n "sr_views")}}
{{raw "topic-list-header-column" sortable=sortable number='true' order='activity' name='activity' ariaLabel=(i18n "sr_activity")}}
{{raw "topic-list-header-column" sortable=sortable number='true' order='views' name='views'}}
{{raw "topic-list-header-column" sortable=sortable number='true' order='activity' name='activity'}}
{{~raw-plugin-outlet name="topic-list-header-after"~}}

View File

@ -19,7 +19,10 @@ export default EmberObject.extend({
@discourseComputed("topic.replyCount", "ratioText")
title(count, ratio) {
return I18n.messageFormat("posts_likes_MF", { count, ratio }).trim();
return I18n.messageFormat("posts_likes_MF", {
count,
ratio,
});
},
@discourseComputed("ratio")

View File

@ -4141,36 +4141,33 @@ en:
pending_posts:
label: "Pending"
label_with_count: "Pending (%{count})"
sr_topic_list_caption: Topic list, column headers with buttons are sortable.
# This string uses the ICU Message Format. See https://meta.discourse.org/t/7035 for translation guidelines.
posts_likes_MF: |
This topic has { count, plural,
one {# reply}
other {# replies}
} { ratio, select,
low {with a high like to post ratio}
med {with a very high like to post ratio}
high {with an extremely high like to post ratio}
{ count, plural,
one {# reply,}
other {# replies,}
}{ ratio, select,
low { high like to post ratio,}
med { very high like to post ratio,}
high { extremely high like to post ratio,}
other {}
}
} jump to the first or last post…
posters: "Posters"
latest_poster_link: "%{username}'s profile, latest poster"
original_post: "Original Post"
views: "Views"
sr_views: "Sort by views"
views_lowercase:
one: "view"
other: "views"
replies: "Replies"
sr_replies: "Sort by replies"
views_long:
one: "this topic has been viewed %{count} time"
other: "this topic has been viewed %{count} times"
activity: "Activity"
sr_activity: "Sort by activity"
likes: "Likes"
sr_likes: "Sort by likes"
sr_op_likes: "Sort by original post likes"
likes_lowercase:
one: "like"
other: "likes"

View File

@ -172,7 +172,7 @@ RSpec.describe JsLocaleHelper do
it "translates messages properly" do
expect(
translated_message,
).to eq "This topic has 3 replies with a very high like to post ratio\n"
).to eq "3 replies, very high like to post ratio, jump to the first or last post…\n"
end
context "when the translation is overriden" do
@ -207,7 +207,7 @@ RSpec.describe JsLocaleHelper do
it "returns the fallback translation" do
expect(
translated_message,
).to eq "This topic has 3 replies with a very high like to post ratio\n"
).to eq "3 replies, very high like to post ratio, jump to the first or last post…\n"
end
context "when the fallback translation is overriden" do