mirror of
https://github.com/discourse/discourse.git
synced 2024-11-26 21:23:59 +08:00
A11Y: Improve accessibility of likes/read count post buttons
The improvements are: * Add an aria-label to the like/read count buttons below posts to indicate what they mean and do. * Add aria-pressed to the like/read count buttons to make it clear to screen readers that these buttons are toggleable. * Add an aria-label to the list of avatars that's shown when post likes or readers are expanded so that screen reader users can understand what the list of avatars means.
This commit is contained in:
parent
acdb64eb7e
commit
fd26facdf3
|
@ -23,6 +23,14 @@ createWidget("small-user-list", {
|
||||||
return atts.listClassName;
|
return atts.listClassName;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
buildAttributes(attrs) {
|
||||||
|
const attributes = { role: "list" };
|
||||||
|
if (attrs.ariaLabel) {
|
||||||
|
attributes["aria-label"] = attrs.ariaLabel;
|
||||||
|
}
|
||||||
|
return attributes;
|
||||||
|
},
|
||||||
|
|
||||||
html(atts) {
|
html(atts) {
|
||||||
let users = atts.users;
|
let users = atts.users;
|
||||||
if (users) {
|
if (users) {
|
||||||
|
@ -37,7 +45,11 @@ createWidget("small-user-list", {
|
||||||
let description = null;
|
let description = null;
|
||||||
|
|
||||||
if (atts.description) {
|
if (atts.description) {
|
||||||
description = I18n.t(atts.description, { count: atts.count });
|
description = h(
|
||||||
|
"span",
|
||||||
|
{ attributes: { "aria-hidden": true } },
|
||||||
|
I18n.t(atts.description, { count: atts.count })
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// oddly post_url is on the user
|
// oddly post_url is on the user
|
||||||
|
@ -46,10 +58,16 @@ createWidget("small-user-list", {
|
||||||
postUrl = postUrl || u.post_url;
|
postUrl = postUrl || u.post_url;
|
||||||
if (u.unknown) {
|
if (u.unknown) {
|
||||||
return h("div.unknown", {
|
return h("div.unknown", {
|
||||||
attributes: { title: I18n.t("post.unknown_user") },
|
attributes: {
|
||||||
|
title: I18n.t("post.unknown_user"),
|
||||||
|
role: "listitem",
|
||||||
|
},
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
return avatarFor.call(this, "small", u);
|
return avatarFor.call(this, "small", u, {
|
||||||
|
role: "listitem",
|
||||||
|
"aria-hidden": false,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -45,6 +45,14 @@ export const ButtonClass = {
|
||||||
attributes["role"] = attrs.role;
|
attributes["role"] = attrs.role;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (attrs.translatedAriaLabel) {
|
||||||
|
attributes["aria-label"] = attrs.translatedAriaLabel;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (attrs.ariaPressed) {
|
||||||
|
attributes["aria-pressed"] = attrs.ariaPressed;
|
||||||
|
}
|
||||||
|
|
||||||
if (attrs.tabAttrs) {
|
if (attrs.tabAttrs) {
|
||||||
const tab = attrs.tabAttrs;
|
const tab = attrs.tabAttrs;
|
||||||
attributes["aria-selected"] = tab["aria-selected"];
|
attributes["aria-selected"] = tab["aria-selected"];
|
||||||
|
|
|
@ -5,6 +5,7 @@ import { formattedReminderTime } from "discourse/lib/bookmark";
|
||||||
import { h } from "virtual-dom";
|
import { h } from "virtual-dom";
|
||||||
import showModal from "discourse/lib/show-modal";
|
import showModal from "discourse/lib/show-modal";
|
||||||
import { smallUserAtts } from "discourse/widgets/actions-summary";
|
import { smallUserAtts } from "discourse/widgets/actions-summary";
|
||||||
|
import I18n from "I18n";
|
||||||
|
|
||||||
const LIKE_ACTION = 2;
|
const LIKE_ACTION = 2;
|
||||||
const VIBRATE_DURATION = 5;
|
const VIBRATE_DURATION = 5;
|
||||||
|
@ -64,10 +65,14 @@ export function buildButton(name, widget) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
registerButton("read-count", (attrs) => {
|
registerButton("read-count", (attrs, state) => {
|
||||||
if (attrs.showReadIndicator) {
|
if (attrs.showReadIndicator) {
|
||||||
const count = attrs.readCount;
|
const count = attrs.readCount;
|
||||||
if (count > 0) {
|
if (count > 0) {
|
||||||
|
let ariaPressed = "false";
|
||||||
|
if (state?.readers && state.readers.length > 0) {
|
||||||
|
ariaPressed = "true";
|
||||||
|
}
|
||||||
return {
|
return {
|
||||||
action: "toggleWhoRead",
|
action: "toggleWhoRead",
|
||||||
title: "post.controls.read_indicator",
|
title: "post.controls.read_indicator",
|
||||||
|
@ -75,6 +80,10 @@ registerButton("read-count", (attrs) => {
|
||||||
contents: count,
|
contents: count,
|
||||||
iconRight: true,
|
iconRight: true,
|
||||||
addContainer: false,
|
addContainer: false,
|
||||||
|
translatedAriaLabel: I18n.t("post.sr_post_read_count_button", {
|
||||||
|
count,
|
||||||
|
}),
|
||||||
|
ariaPressed,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -93,7 +102,7 @@ registerButton("read", (attrs) => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
function likeCount(attrs) {
|
function likeCount(attrs, state) {
|
||||||
const count = attrs.likeCount;
|
const count = attrs.likeCount;
|
||||||
|
|
||||||
if (count > 0) {
|
if (count > 0) {
|
||||||
|
@ -111,6 +120,10 @@ function likeCount(attrs) {
|
||||||
addContainer = true;
|
addContainer = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let ariaPressed = "false";
|
||||||
|
if (state?.likedUsers && state.likedUsers.length > 0) {
|
||||||
|
ariaPressed = "true";
|
||||||
|
}
|
||||||
return {
|
return {
|
||||||
action: "toggleWhoLiked",
|
action: "toggleWhoLiked",
|
||||||
title,
|
title,
|
||||||
|
@ -120,6 +133,8 @@ function likeCount(attrs) {
|
||||||
iconRight: true,
|
iconRight: true,
|
||||||
addContainer,
|
addContainer,
|
||||||
titleOptions: { count: attrs.liked ? count - 1 : count },
|
titleOptions: { count: attrs.liked ? count - 1 : count },
|
||||||
|
translatedAriaLabel: I18n.t("post.sr_post_like_count_button", { count }),
|
||||||
|
ariaPressed,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -630,6 +645,9 @@ export default createWidget("post-menu", {
|
||||||
listClassName: "who-read",
|
listClassName: "who-read",
|
||||||
description,
|
description,
|
||||||
count,
|
count,
|
||||||
|
ariaLabel: I18n.t(
|
||||||
|
"post.actions.people.sr_post_readers_list_description"
|
||||||
|
),
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -649,6 +667,9 @@ export default createWidget("post-menu", {
|
||||||
listClassName: "who-liked",
|
listClassName: "who-liked",
|
||||||
description,
|
description,
|
||||||
count,
|
count,
|
||||||
|
ariaLabel: I18n.t(
|
||||||
|
"post.actions.people.sr_post_likers_list_description"
|
||||||
|
),
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -68,16 +68,20 @@ export function avatarImg(wanted, attrs) {
|
||||||
return h("img", properties);
|
return h("img", properties);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function avatarFor(wanted, attrs) {
|
export function avatarFor(wanted, attrs, linkAttrs) {
|
||||||
|
const attributes = {
|
||||||
|
href: attrs.url,
|
||||||
|
"data-user-card": attrs.username,
|
||||||
|
"aria-hidden": true,
|
||||||
|
};
|
||||||
|
if (linkAttrs) {
|
||||||
|
Object.assign(attributes, linkAttrs);
|
||||||
|
}
|
||||||
return h(
|
return h(
|
||||||
"a",
|
"a",
|
||||||
{
|
{
|
||||||
className: `trigger-user-card ${attrs.className || ""}`,
|
className: `trigger-user-card ${attrs.className || ""}`,
|
||||||
attributes: {
|
attributes,
|
||||||
href: attrs.url,
|
|
||||||
"data-user-card": attrs.username,
|
|
||||||
"aria-hidden": true,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
avatarImg(wanted, attrs)
|
avatarImg(wanted, attrs)
|
||||||
);
|
);
|
||||||
|
|
|
@ -3091,6 +3091,12 @@ en:
|
||||||
one: "you and %{count} other person liked this post"
|
one: "you and %{count} other person liked this post"
|
||||||
other: "you and %{count} other people liked this post"
|
other: "you and %{count} other people liked this post"
|
||||||
|
|
||||||
|
sr_post_like_count_button:
|
||||||
|
one: "%{count} person liked this post. Click to view"
|
||||||
|
other: "%{count} people liked this post. Click to view"
|
||||||
|
sr_post_read_count_button:
|
||||||
|
one: "%{count} person read this post. Click to view"
|
||||||
|
other: "%{count} people read this post. Click to view"
|
||||||
filtered_replies_hint:
|
filtered_replies_hint:
|
||||||
one: "View this post and its reply"
|
one: "View this post and its reply"
|
||||||
other: "View this post and its %{count} replies"
|
other: "View this post and its %{count} replies"
|
||||||
|
@ -3201,6 +3207,8 @@ en:
|
||||||
read_capped:
|
read_capped:
|
||||||
one: "and %{count} other read this"
|
one: "and %{count} other read this"
|
||||||
other: "and %{count} others read this"
|
other: "and %{count} others read this"
|
||||||
|
sr_post_likers_list_description: "users who liked this post"
|
||||||
|
sr_post_readers_list_description: "users who read this post"
|
||||||
by_you:
|
by_you:
|
||||||
off_topic: "You flagged this as off-topic"
|
off_topic: "You flagged this as off-topic"
|
||||||
spam: "You flagged this as spam"
|
spam: "You flagged this as spam"
|
||||||
|
|
Loading…
Reference in New Issue
Block a user