A11Y: Signal the toggle header can reorder table elements. (#18597)

While navigating the table, indicate what the button does and if the user pressed it or not. Also, don't lose focus after reordering elements.
This commit is contained in:
Roman Rizzi 2022-10-14 13:28:20 -03:00 committed by GitHub
parent 6b4b279141
commit 2933baa0ce
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 74 additions and 16 deletions

View File

@ -15,22 +15,24 @@
<LoadMore @class="users-list-container" @selector=".users-list tr" @action={{action "loadMore"}}>
{{#if this.model}}
<table class="table users-list grid">
<table class="table users-list grid" role="table" aria-label={{this.title}}>
<thead>
<TableHeaderToggle @field="username" @labelKey="username" @order={{this.order}} @asc={{this.asc}} @automatic={{true}} />
<TableHeaderToggle @class={{if this.showEmails "" "hidden"}} @field="email" @labelKey="email" @order={{this.order}} @asc={{this.asc}} @automatic={{true}} />
<TableHeaderToggle @field="last_emailed" @labelKey="admin.users.last_emailed" @order={{this.order}} @asc={{this.asc}} @automatic={{true}} />
<TableHeaderToggle @field="seen" @labelKey="last_seen" @order={{this.order}} @asc={{this.asc}} @automatic={{true}} />
<TableHeaderToggle @field="topics_viewed" @labelKey="admin.user.topics_entered" @order={{this.order}} @asc={{this.asc}} @automatic={{true}} />
<TableHeaderToggle @field="posts_read" @labelKey="admin.user.posts_read_count" @order={{this.order}} @asc={{this.asc}} @automatic={{true}} />
<TableHeaderToggle @field="read_time" @labelKey="admin.user.time_read" @order={{this.order}} @asc={{this.asc}} @automatic={{true}} />
<TableHeaderToggle @field="created" @labelKey="created" @order={{this.order}} @asc={{this.asc}} @automatic={{true}} />
<PluginOutlet @name="admin-users-list-thead-after" @args={{hash order=this.order asc=this.asc}} />
<tr>
<TableHeaderToggle @field="username" @labelKey="username" @order={{this.order}} @asc={{this.asc}} @automatic={{true}} />
<TableHeaderToggle @class={{if this.showEmails "" "hidden"}} @field="email" @labelKey="email" @order={{this.order}} @asc={{this.asc}} @automatic={{true}} />
<TableHeaderToggle @field="last_emailed" @labelKey="admin.users.last_emailed" @order={{this.order}} @asc={{this.asc}} @automatic={{true}} />
<TableHeaderToggle @field="seen" @labelKey="last_seen" @order={{this.order}} @asc={{this.asc}} @automatic={{true}} />
<TableHeaderToggle @field="topics_viewed" @labelKey="admin.user.topics_entered" @order={{this.order}} @asc={{this.asc}} @automatic={{true}} />
<TableHeaderToggle @field="posts_read" @labelKey="admin.user.posts_read_count" @order={{this.order}} @asc={{this.asc}} @automatic={{true}} />
<TableHeaderToggle @field="read_time" @labelKey="admin.user.time_read" @order={{this.order}} @asc={{this.asc}} @automatic={{true}} />
<TableHeaderToggle @field="created" @labelKey="created" @order={{this.order}} @asc={{this.asc}} @automatic={{true}} />
<PluginOutlet @name="admin-users-list-thead-after" @args={{hash order=this.order asc=this.asc}} />
{{#if this.siteSettings.must_approve_users}}
<th>{{i18n "admin.users.approved"}}</th>
{{/if}}
<th>&nbsp;</th>
{{#if this.siteSettings.must_approve_users}}
<th>{{i18n "admin.users.approved"}}</th>
{{/if}}
<th>&nbsp;</th>
</tr>
</thead>
<tbody>
{{#each this.model as |user|}}

View File

@ -1,18 +1,32 @@
import Component from "@ember/component";
import { iconHTML } from "discourse-common/lib/icon-library";
import { htmlSafe } from "@ember/template";
import { schedule } from "@ember/runloop";
import discourseComputed from "discourse-common/utils/decorators";
import I18n from "I18n";
export default Component.extend({
tagName: "th",
classNames: ["sortable"],
attributeBindings: ["title", "colspan"],
attributeBindings: ["title", "colspan", "ariaSort:aria-sort", "role"],
role: "columnheader",
labelKey: null,
chevronIcon: null,
columnIcon: null,
translated: false,
automatic: false,
onActiveRender: null,
pressedState: null,
ariaLabel: null,
@discourseComputed("order", "field", "asc")
ariaSort() {
if (this.order === this.field) {
return this.asc ? "ascending" : "descending";
} else {
return "none";
}
},
toggleProperties() {
if (this.order === this.field) {
this.set("asc", this.asc ? null : true);
@ -43,10 +57,48 @@ export default Component.extend({
}
this.set("id", `table-header-toggle-${this.field.replace(/\s/g, "")}`);
this.toggleChevron();
this._updateA11yAttributes();
},
didRender() {
if (this.onActiveRender && this.chevronIcon) {
this.onActiveRender(this.element);
}
},
_updateA11yAttributes() {
let criteria = "";
const pressed = this.order === this.field;
if (this.icon === "heart") {
criteria += `${I18n.t("likes_lowercase", { count: 2 })} `;
}
if (this.translated) {
criteria += this.field;
} else {
const labelKey = this.labelKey || `directory.${this.field}`;
criteria += I18n.t(labelKey + "_long", {
defaultValue: I18n.t(labelKey),
});
}
this.set("ariaLabel", I18n.t("directory.sort.label", { criteria }));
if (pressed) {
if (this.asc) {
this.set("pressedState", "mixed");
} else {
this.set("pressedState", "true");
}
this._focusHeader();
} else {
this.set("pressedState", "false");
}
},
_focusHeader() {
schedule("afterRender", () => {
document.getElementById(this.id)?.focus();
});
},
});

View File

@ -2,7 +2,9 @@
class="header-contents"
id={{this.id}}
role="button"
tabindex="0">
tabindex="0"
aria-label={{this.ariaLabel}}
aria-pressed={{this.pressedState}}>
{{directory-table-header-title field=this.field labelKey=this.labelKey icon=this.icon translated=this.translated}}
{{this.chevronIcon}}
</span>

View File

@ -720,6 +720,8 @@ en:
reset_to_default: "Reset to default"
group:
all: "all groups"
sort:
label: "Sort by %{criteria}"
group_histories:
actions: