Skip to content
This repository has been archived by the owner on Sep 11, 2024. It is now read-only.

Accessibility: Improve Voiceover #12189

Closed
wants to merge 14 commits into from
12 changes: 12 additions & 0 deletions src/components/views/avatars/MemberAvatar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ interface IProps extends Omit<React.ComponentProps<typeof BaseAvatar>, "name" |
forceHistorical?: boolean; // true to deny `useOnlyCurrentProfiles` usage. Default false.
hideTitle?: boolean;
children?: ReactNode;
// Will append a dot to the aria-label if true
includePauseInAriaLabel?: boolean;
}

function MemberAvatar(
Expand All @@ -51,6 +53,7 @@ function MemberAvatar(
fallbackUserId,
hideTitle,
member: propsMember,
includePauseInAriaLabel,
...props
}: IProps,
ref: Ref<HTMLElement>,
Expand Down Expand Up @@ -83,9 +86,17 @@ function MemberAvatar(
}
}

/**
* If this avatar is rendered in the event tile before the message, we append a dot to the
* aria-label so that assistive technologies read out `name <pause> message-content` instead
* of `name message-content`.
*/
const ariaLabel = includePauseInAriaLabel ? `${member?.name}. ` : member?.name;

return (
<BaseAvatar
{...props}
tabIndex={-1}
size={size}
name={name ?? ""}
title={hideTitle ? undefined : title}
Expand All @@ -102,6 +113,7 @@ function MemberAvatar(
}
: props.onClick
}
aria-label={ariaLabel ?? ""}
altText={_t("common|user_avatar")}
ref={ref}
/>
Expand Down
7 changes: 4 additions & 3 deletions src/components/views/messages/TextualBody.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -612,6 +612,7 @@ export default class TextualBody extends React.Component<IBodyProps, IState> {
}

let widgets;
const id = "mx_EventTile_content_" + mxEvent.getId();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

getId() here will actually return the local id but will return the remote id from sync elsewhere:

image

Probably best to use mxEvent.getTxnId() ?? mxEvent.getId()

if (this.state.links.length && !this.state.widgetHidden && this.props.showUrlPreview) {
widgets = (
<LinkPreviewGroup
Expand All @@ -625,7 +626,7 @@ export default class TextualBody extends React.Component<IBodyProps, IState> {

if (isEmote) {
return (
<div className="mx_MEmoteBody mx_EventTile_content" onClick={this.onBodyLinkClick}>
<div className="mx_MEmoteBody mx_EventTile_content" id={id} onClick={this.onBodyLinkClick}>
*&nbsp;
<span className="mx_MEmoteBody_sender" onClick={this.onEmoteSenderClick}>
{mxEvent.sender ? mxEvent.sender.name : mxEvent.getSender()}
Expand All @@ -638,14 +639,14 @@ export default class TextualBody extends React.Component<IBodyProps, IState> {
}
if (isNotice) {
return (
<div className="mx_MNoticeBody mx_EventTile_content" onClick={this.onBodyLinkClick}>
<div className="mx_MNoticeBody mx_EventTile_content" id={id} onClick={this.onBodyLinkClick}>
{body}
{widgets}
</div>
);
}
return (
<div className="mx_MTextBody mx_EventTile_content" onClick={this.onBodyLinkClick}>
<div className="mx_MTextBody mx_EventTile_content" id={id} onClick={this.onBodyLinkClick}>
{body}
{widgets}
</div>
Expand Down
23 changes: 19 additions & 4 deletions src/components/views/rooms/EventTile.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -1061,12 +1061,17 @@ export class UnwrappedEventTile extends React.Component<EventTileProps, IState>
this.context.timelineRenderingType,
);
avatar = (
<div className="mx_EventTile_avatar">
<div
className="mx_EventTile_avatar"
id={"mx_EventTile_avatar_" + this.props.mxEvent.getId()}
tabIndex={-1}
>
<MemberAvatar
member={member}
size={avatarSize}
viewUserOnClick={viewUserOnClick}
forceHistorical={this.props.mxEvent.getType() === EventType.RoomMember}
includePauseInAriaLabel={true}
/>
</div>
);
Expand Down Expand Up @@ -1147,6 +1152,7 @@ export class UnwrappedEventTile extends React.Component<EventTileProps, IState>
<a
href={permalink}
onClick={this.onPermalinkClicked}
tabIndex={-1}
aria-label={formatTime(new Date(this.props.mxEvent.getTs()), this.props.isTwelveHour)}
onContextMenu={this.onTimestampContextMenu}
>
Expand Down Expand Up @@ -1207,7 +1213,9 @@ export class UnwrappedEventTile extends React.Component<EventTileProps, IState>
{
"ref": this.ref,
"className": classes,
"tabIndex": 0,
"aria-live": ariaLive,
"aria-labelledby": `mx_EventTile_avatar_${this.props.mxEvent.getId()} mx_EventTile_content_${this.props.mxEvent.getId()}`,
"aria-atomic": true,
"data-scroll-tokens": scrollToken,
"data-has-reply": !!replyChain,
Expand Down Expand Up @@ -1261,8 +1269,9 @@ export class UnwrappedEventTile extends React.Component<EventTileProps, IState>
{
"ref": this.ref,
"className": classes,
"tabIndex": -1,
"tabindex": 0,
"aria-live": ariaLive,
"aria-labelledby": `mx_EventTile_avatar_${this.props.mxEvent.getId()} mx_EventTile_content_${this.props.mxEvent.getId()}`,
"aria-atomic": "true",
"data-scroll-tokens": scrollToken,
"data-layout": this.props.layout,
Expand Down Expand Up @@ -1395,8 +1404,9 @@ export class UnwrappedEventTile extends React.Component<EventTileProps, IState>
{
"ref": this.ref,
"className": classes,
"tabIndex": -1,
"tabindex": 0,
"aria-live": ariaLive,
"aria-labelledby": `mx_EventTile_avatar_${this.props.mxEvent.getId()} mx_EventTile_content_${this.props.mxEvent.getId()}`,
"aria-atomic": "true",
"data-scroll-tokens": scrollToken,
"data-layout": this.props.layout,
Expand All @@ -1411,7 +1421,12 @@ export class UnwrappedEventTile extends React.Component<EventTileProps, IState>
{sender}
{ircPadlock}
{avatar}
<div className={lineClasses} key="mx_EventTile_line" onContextMenu={this.onContextMenu}>
<div
className={lineClasses}
key="mx_EventTile_line"
onContextMenu={this.onContextMenu}
aria-labelledby={"mx_EventTile_content_" + this.props.mxEvent.getId()}
>
{this.renderContextMenu()}
{groupTimestamp}
{groupPadlock}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,12 +94,14 @@ exports[`MessagePanel should handle lots of membership events quickly 1`] = `
class="mx_GenericEventListSummary_avatars"
>
<span
aria-label="@user:id"
class="_avatar_mcap2_17 mx_BaseAvatar _avatar-imageless_mcap2_61"
data-color="1"
data-testid="avatar-img"
data-type="round"
role="presentation"
style="--cpd-avatar-size: 14px;"
tabindex="-1"
title="@user:id"
>
u
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,14 @@ exports[`<BeaconMarker /> renders marker when beacon has location 1`] = `
class="mx_Marker_border"
>
<span
aria-label="@alice:server"
class="_avatar_mcap2_17 mx_BaseAvatar _avatar-imageless_mcap2_61"
data-color="6"
data-testid="avatar-img"
data-type="round"
role="presentation"
style="--cpd-avatar-size: 36px;"
tabindex="-1"
title="@alice:server"
>
a
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,14 @@ exports[`<BeaconViewDialog /> renders own beacon status when user is live sharin
class="mx_DialogOwnBeaconStatus"
>
<span
aria-label="@alice:server"
class="_avatar_mcap2_17 mx_BaseAvatar mx_DialogOwnBeaconStatus_avatar _avatar-imageless_mcap2_61"
data-color="6"
data-testid="avatar-img"
data-type="round"
role="presentation"
style="--cpd-avatar-size: 32px;"
tabindex="-1"
title="@alice:server"
>
a
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,14 @@ exports[`<DialogSidebar /> renders sidebar correctly with beacons 1`] = `
class="mx_BeaconListItem"
>
<span
aria-label=""
class="_avatar_mcap2_17 mx_BaseAvatar mx_BeaconListItem_avatar _avatar-imageless_mcap2_61"
data-color="1"
data-testid="avatar-img"
data-type="round"
role="presentation"
style="--cpd-avatar-size: 32px;"
tabindex="-1"
>

</span>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,14 @@ exports[`ConfirmUserActionDialog renders 1`] = `
class="mx_ConfirmUserActionDialog_avatar"
>
<span
aria-label="@user:test.com"
class="_avatar_mcap2_17 mx_BaseAvatar _avatar-imageless_mcap2_61"
data-color="3"
data-testid="avatar-img"
data-type="round"
role="presentation"
style="--cpd-avatar-size: 48px;"
tabindex="-1"
title="@user:test.com"
>
u
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,14 @@ exports[`<FacePile /> renders with a tooltip 1`] = `
class="_stacked-avatars_mcap2_111"
>
<span
aria-label="456"
class="_avatar_mcap2_17 mx_BaseAvatar _avatar-imageless_mcap2_61"
data-color="4"
data-testid="avatar-img"
data-type="round"
role="presentation"
style="--cpd-avatar-size: 36px;"
tabindex="-1"
>
4
</span>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,12 +71,14 @@ exports[`<Pill> should render the expected pill for a known user not in the room
>
<span
aria-hidden="true"
aria-label="User 2"
class="_avatar_mcap2_17 mx_BaseAvatar _avatar-imageless_mcap2_61"
data-color="5"
data-testid="avatar-img"
data-type="round"
role="presentation"
style="--cpd-avatar-size: 16px;"
tabindex="-1"
>
U
</span>
Expand Down Expand Up @@ -131,12 +133,14 @@ exports[`<Pill> should render the expected pill for a message in the same room 1
>
<span
aria-hidden="true"
aria-label="User 1"
class="_avatar_mcap2_17 mx_BaseAvatar _avatar-imageless_mcap2_61"
data-color="4"
data-testid="avatar-img"
data-type="round"
role="presentation"
style="--cpd-avatar-size: 16px;"
tabindex="-1"
>
U
</span>
Expand Down Expand Up @@ -273,12 +277,14 @@ exports[`<Pill> when rendering a pill for a user in the room should render as ex
>
<span
aria-hidden="true"
aria-label="User 1"
class="_avatar_mcap2_17 mx_BaseAvatar _avatar-imageless_mcap2_61"
data-color="4"
data-testid="avatar-img"
data-type="round"
role="presentation"
style="--cpd-avatar-size: 16px;"
tabindex="-1"
>
U
</span>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,14 @@ exports[`<RoomFacePile /> renders 1`] = `
class="_stacked-avatars_mcap2_111"
>
<span
aria-label="@bob:example.org"
class="_avatar_mcap2_17 mx_BaseAvatar _avatar-imageless_mcap2_61"
data-color="4"
data-testid="avatar-img"
data-type="round"
role="presentation"
style="--cpd-avatar-size: 28px;"
tabindex="-1"
>
b
</span>
Expand Down
2 changes: 1 addition & 1 deletion test/components/views/messages/TextualBody-test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,7 @@ describe("<TextualBody />", () => {
const { container } = getComponent({ mxEvent: ev });
const content = container.querySelector(".mx_EventTile_body");
expect(content.innerHTML).toMatchInlineSnapshot(
`"Chat with <span><bdi><a class="mx_Pill mx_UserPill mx_UserPill_me" href="https://matrix.to/#/@user:example.com"><span aria-label="Profile picture" aria-hidden="true" data-testid="avatar-img" data-type="round" data-color="2" class="_avatar_mcap2_17 mx_BaseAvatar" style="--cpd-avatar-size: 16px;"><img loading="lazy" alt="" src="mxc://avatar.url/image.png" referrerpolicy="no-referrer" class="_image_mcap2_50" data-type="round" width="16px" height="16px"></span><span class="mx_Pill_text">Member</span></a></bdi></span>"`,
`"Chat with <span><bdi><a class="mx_Pill mx_UserPill mx_UserPill_me" href="https://matrix.to/#/@user:example.com"><span aria-label="Member" aria-hidden="true" tabindex="-1" data-testid="avatar-img" data-type="round" data-color="2" class="_avatar_mcap2_17 mx_BaseAvatar" style="--cpd-avatar-size: 16px;"><img loading="lazy" alt="" src="mxc://avatar.url/image.png" referrerpolicy="no-referrer" class="_image_mcap2_50" data-type="round" width="16px" height="16px"></span><span class="mx_Pill_text">Member</span></a></bdi></span>"`,
);
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,12 +83,14 @@ exports[`MLocationBody <MLocationBody> without error renders marker correctly fo
class="mx_Marker_border"
>
<span
aria-label="@user:server"
class="_avatar_mcap2_17 mx_BaseAvatar _avatar-imageless_mcap2_61"
data-color="3"
data-testid="avatar-img"
data-type="round"
role="presentation"
style="--cpd-avatar-size: 36px;"
tabindex="-1"
title="@user:server"
>
u
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,12 +55,13 @@ exports[`<TextualBody /> renders formatted m.text correctly pills appear for an
>
<span
aria-hidden="true"
aria-label="Profile picture"
aria-label="Member"
class="_avatar_mcap2_17 mx_BaseAvatar"
data-color="2"
data-testid="avatar-img"
data-type="round"
style="--cpd-avatar-size: 16px;"
tabindex="-1"
>
<img
alt=""
Expand Down Expand Up @@ -88,6 +89,7 @@ exports[`<TextualBody /> renders formatted m.text correctly pills appear for eve
<DocumentFragment>
<div
class="mx_MTextBody mx_EventTile_content"
id="mx_EventTile_content_$0.123456-0.123456"
>
<span
class="mx_EventTile_body markdown-body"
Expand Down Expand Up @@ -137,6 +139,7 @@ exports[`<TextualBody /> renders formatted m.text correctly pills appear for roo
<DocumentFragment>
<div
class="mx_MTextBody mx_EventTile_content"
id="mx_EventTile_content_$0.123456-0.123456"
>
<span
class="mx_EventTile_body markdown-body"
Expand Down Expand Up @@ -187,6 +190,7 @@ exports[`<TextualBody /> renders formatted m.text correctly pills do not appear
<DocumentFragment>
<div
class="mx_MTextBody mx_EventTile_content"
id="mx_EventTile_content_$0.123456-0.123456"
>
<span
class="mx_EventTile_body markdown-body"
Expand Down Expand Up @@ -259,12 +263,13 @@ exports[`<TextualBody /> renders formatted m.text correctly pills get injected c
>
<span
aria-hidden="true"
aria-label="Profile picture"
aria-label="Member"
class="_avatar_mcap2_17 mx_BaseAvatar"
data-color="2"
data-testid="avatar-img"
data-type="round"
style="--cpd-avatar-size: 16px;"
tabindex="-1"
>
<img
alt=""
Expand All @@ -288,7 +293,7 @@ exports[`<TextualBody /> renders formatted m.text correctly pills get injected c
</span>
`;

exports[`<TextualBody /> renders plain-text m.text correctly should pillify a permalink to a message in the same room with the label »Message from Member« 1`] = `"Visit <span><bdi><a class="mx_Pill mx_EventPill" href="https://matrix.to/#/!room1:example.com/%event_id%"><span aria-label="Profile picture" aria-hidden="true" data-testid="avatar-img" data-type="round" data-color="2" class="_avatar_mcap2_17 mx_BaseAvatar" style="--cpd-avatar-size: 16px;"><img loading="lazy" alt="" src="mxc://avatar.url/image.png" referrerpolicy="no-referrer" class="_image_mcap2_50" data-type="round" width="16px" height="16px"></span><span class="mx_Pill_text">Message from Member</span></a></bdi></span>"`;
exports[`<TextualBody /> renders plain-text m.text correctly should pillify a permalink to a message in the same room with the label »Message from Member« 1`] = `"Visit <span><bdi><a class="mx_Pill mx_EventPill" href="https://matrix.to/#/!room1:example.com/%event_id%"><span aria-label="Member" aria-hidden="true" tabindex="-1" data-testid="avatar-img" data-type="round" data-color="2" class="_avatar_mcap2_17 mx_BaseAvatar" style="--cpd-avatar-size: 16px;"><img loading="lazy" alt="" src="mxc://avatar.url/image.png" referrerpolicy="no-referrer" class="_image_mcap2_50" data-type="round" width="16px" height="16px"></span><span class="mx_Pill_text">Message from Member</span></a></bdi></span>"`;

exports[`<TextualBody /> renders plain-text m.text correctly should pillify a permalink to an event in another room with the label »Message in Room 2« 1`] = `"Visit <span><bdi><a class="mx_Pill mx_EventPill" href="https://matrix.to/#/!room2:example.com/%event_id%"><span aria-label="Avatar" aria-hidden="true" data-testid="avatar-img" data-type="round" data-color="2" class="_avatar_mcap2_17 mx_BaseAvatar" style="--cpd-avatar-size: 16px;"><img loading="lazy" alt="" src="mxc://avatar.url/room.png" referrerpolicy="no-referrer" class="_image_mcap2_50" data-type="round" width="16px" height="16px"></span><span class="mx_Pill_text">Message in Room 2</span></a></bdi></span>"`;

Expand Down
Loading
Loading