A11y: improve accessibility of pinned messages (#30558)

* fix: improve aria role and label on pinned message banner

* fix: change pinned message badge background for contrast

* fix: link pinned message button to content

* test: update tests

* fix: add aria-describedby on pinned message badge

* feat: use `aria-describedby` instead of `aria-description`

* test: update room view snapshot

* test: update snapshot

* fix: put id only textual body upper div

* fix: use lodash uniqueId

* test: update snapshots
This commit is contained in:
Florian Duros
2025-09-02 15:03:01 +02:00
committed by GitHub
parent 1925132a3c
commit eb086bd795
17 changed files with 205 additions and 102 deletions

View File

@@ -19,7 +19,7 @@ exports[`<PinnedMessagesCard /> should show the empty state when there are no pi
</p>
</div>
<button
aria-labelledby="«re»"
aria-labelledby="«rf»"
class="_icon-button_1pz9o_8"
data-kind="secondary"
data-testid="base-card-close-button"
@@ -101,7 +101,7 @@ exports[`<PinnedMessagesCard /> should show two pinned messages 1`] = `
</p>
</div>
<button
aria-labelledby="«rk»"
aria-labelledby="«rl»"
class="_icon-button_1pz9o_8"
data-kind="secondary"
data-testid="base-card-close-button"
@@ -159,12 +159,13 @@ exports[`<PinnedMessagesCard /> should show two pinned messages 1`] = `
class="mx_PinnedEventTile_top"
>
<span
aria-labelledby="«rq»"
aria-labelledby="«rs»"
class="mx_PinnedEventTile_sender mx_Username_color3"
>
@alice:example.org
</span>
<button
aria-describedby="«rr»"
aria-disabled="false"
aria-expanded="false"
aria-haspopup="menu"
@@ -172,7 +173,7 @@ exports[`<PinnedMessagesCard /> should show two pinned messages 1`] = `
class="_icon-button_1pz9o_8"
data-kind="primary"
data-state="closed"
id="radix-«rv»"
id="radix-«r11»"
role="button"
style="--cpd-icon-button-size: 24px;"
tabindex="0"
@@ -198,6 +199,7 @@ exports[`<PinnedMessagesCard /> should show two pinned messages 1`] = `
</div>
<div
class="mx_MTextBody mx_EventTile_content"
id="«rr»"
>
<div
class="mx_EventTile_body translate"
@@ -238,12 +240,13 @@ exports[`<PinnedMessagesCard /> should show two pinned messages 1`] = `
class="mx_PinnedEventTile_top"
>
<span
aria-labelledby="«r11»"
aria-labelledby="«r14»"
class="mx_PinnedEventTile_sender mx_Username_color3"
>
@alice:example.org
</span>
<button
aria-describedby="«r13»"
aria-disabled="false"
aria-expanded="false"
aria-haspopup="menu"
@@ -251,7 +254,7 @@ exports[`<PinnedMessagesCard /> should show two pinned messages 1`] = `
class="_icon-button_1pz9o_8"
data-kind="primary"
data-state="closed"
id="radix-«r16»"
id="radix-«r19»"
role="button"
style="--cpd-icon-button-size: 24px;"
tabindex="0"
@@ -277,6 +280,7 @@ exports[`<PinnedMessagesCard /> should show two pinned messages 1`] = `
</div>
<div
class="mx_MTextBody mx_EventTile_content"
id="«r13»"
>
<div
class="mx_EventTile_body translate"
@@ -325,7 +329,7 @@ exports[`<PinnedMessagesCard /> unpin all should not allow to unpinall 1`] = `
</p>
</div>
<button
aria-labelledby="«rt2»"
aria-labelledby="«r10f»"
class="_icon-button_1pz9o_8"
data-kind="secondary"
data-testid="base-card-close-button"
@@ -383,12 +387,13 @@ exports[`<PinnedMessagesCard /> unpin all should not allow to unpinall 1`] = `
class="mx_PinnedEventTile_top"
>
<span
aria-labelledby="«rt8»"
aria-labelledby="«r10m»"
class="mx_PinnedEventTile_sender mx_Username_color3"
>
@alice:example.org
</span>
<button
aria-describedby="«r10l»"
aria-disabled="false"
aria-expanded="false"
aria-haspopup="menu"
@@ -396,7 +401,7 @@ exports[`<PinnedMessagesCard /> unpin all should not allow to unpinall 1`] = `
class="_icon-button_1pz9o_8"
data-kind="primary"
data-state="closed"
id="radix-«rtd»"
id="radix-«r10r»"
role="button"
style="--cpd-icon-button-size: 24px;"
tabindex="0"
@@ -422,6 +427,7 @@ exports[`<PinnedMessagesCard /> unpin all should not allow to unpinall 1`] = `
</div>
<div
class="mx_MTextBody mx_EventTile_content"
id="«r10l»"
>
<div
class="mx_EventTile_body translate"
@@ -462,12 +468,13 @@ exports[`<PinnedMessagesCard /> unpin all should not allow to unpinall 1`] = `
class="mx_PinnedEventTile_top"
>
<span
aria-labelledby="«rtf»"
aria-labelledby="«r10u»"
class="mx_PinnedEventTile_sender mx_Username_color3"
>
@alice:example.org
</span>
<button
aria-describedby="«r10t»"
aria-disabled="false"
aria-expanded="false"
aria-haspopup="menu"
@@ -475,7 +482,7 @@ exports[`<PinnedMessagesCard /> unpin all should not allow to unpinall 1`] = `
class="_icon-button_1pz9o_8"
data-kind="primary"
data-state="closed"
id="radix-«rtk»"
id="radix-«r113»"
role="button"
style="--cpd-icon-button-size: 24px;"
tabindex="0"
@@ -501,6 +508,7 @@ exports[`<PinnedMessagesCard /> unpin all should not allow to unpinall 1`] = `
</div>
<div
class="mx_MTextBody mx_EventTile_content"
id="«r10t»"
>
<div
class="mx_EventTile_body translate"

View File

@@ -139,7 +139,12 @@ describe("<PinnedMessageBanner />", () => {
jest.spyOn(pinnedEventHooks, "useSortedFetchedPinnedEvents").mockReturnValue([event1, event2]);
const { asFragment, rerender } = renderBanner();
await userEvent.click(screen.getByRole("button", { name: "View the pinned message in the timeline." }));
await expect(screen.findByText("Second pinned message")).resolves.toBeVisible();
await userEvent.click(
screen.getByRole("button", {
name: "View the pinned message in the timeline and the newest pinned message here",
}),
);
expect(screen.getByText("First pinned message")).toBeVisible();
jest.spyOn(pinnedEventHooks, "usePinnedEvents").mockReturnValue([
@@ -162,7 +167,11 @@ describe("<PinnedMessageBanner />", () => {
renderBanner();
await expect(screen.findByText("Second pinned message")).resolves.toBeVisible();
await userEvent.click(screen.getByRole("button", { name: "View the pinned message in the timeline." }));
await userEvent.click(
screen.getByRole("button", {
name: "View the pinned message in the timeline and the newest pinned message here",
}),
);
expect(screen.getByText("First pinned message")).toBeVisible();
expect(screen.getByTestId("banner-counter")).toHaveTextContent("1 of 2 Pinned messages");
expect(dis.dispatch).toHaveBeenCalledWith({
@@ -173,7 +182,11 @@ describe("<PinnedMessageBanner />", () => {
metricsTrigger: undefined, // room doesn't change
});
await userEvent.click(screen.getByRole("button", { name: "View the pinned message in the timeline." }));
await userEvent.click(
screen.getByRole("button", {
name: "View the pinned message in the timeline and the next oldest pinned message here",
}),
);
expect(screen.getByText("Second pinned message")).toBeVisible();
expect(screen.getByTestId("banner-counter")).toHaveTextContent("2 of 2 Pinned messages");
expect(dis.dispatch).toHaveBeenCalledWith({
@@ -224,7 +237,11 @@ describe("<PinnedMessageBanner />", () => {
// The banner is displayed, so we need to resize the timeline
expect(resizeNotifier.notifyTimelineHeightChanged).toHaveBeenCalledTimes(1);
await userEvent.click(screen.getByRole("button", { name: "View the pinned message in the timeline." }));
await userEvent.click(
screen.getByRole("button", {
name: "View the pinned message in the timeline and the newest pinned message here",
}),
);
await expect(screen.findByText("First pinned message")).resolves.toBeVisible();
// The banner is already displayed, so we don't need to resize the timeline
expect(resizeNotifier.notifyTimelineHeightChanged).toHaveBeenCalledTimes(1);

View File

@@ -25,12 +25,13 @@ exports[`<PinnedEventTile /> should render pinned event 1`] = `
class="mx_PinnedEventTile_top"
>
<span
aria-labelledby="«r0»"
aria-labelledby="«r1»"
class="mx_PinnedEventTile_sender mx_Username_color2"
>
@alice:server.org
</span>
<button
aria-describedby="«r0»"
aria-disabled="false"
aria-expanded="false"
aria-haspopup="menu"
@@ -38,7 +39,7 @@ exports[`<PinnedEventTile /> should render pinned event 1`] = `
class="_icon-button_1pz9o_8"
data-kind="primary"
data-state="closed"
id="radix-«r5»"
id="radix-«r6»"
role="button"
style="--cpd-icon-button-size: 24px;"
tabindex="0"
@@ -64,6 +65,7 @@ exports[`<PinnedEventTile /> should render pinned event 1`] = `
</div>
<div
class="mx_MTextBody mx_EventTile_content"
id="«r0»"
>
<div
class="mx_EventTile_body translate"
@@ -102,12 +104,13 @@ exports[`<PinnedEventTile /> should render pinned event with thread info 1`] = `
class="mx_PinnedEventTile_top"
>
<span
aria-labelledby="«r8»"
aria-labelledby="«ra»"
class="mx_PinnedEventTile_sender mx_Username_color2"
>
@alice:server.org
</span>
<button
aria-describedby="«r9»"
aria-disabled="false"
aria-expanded="false"
aria-haspopup="menu"
@@ -115,7 +118,7 @@ exports[`<PinnedEventTile /> should render pinned event with thread info 1`] = `
class="_icon-button_1pz9o_8"
data-kind="primary"
data-state="closed"
id="radix-«rd»"
id="radix-«rf»"
role="button"
style="--cpd-icon-button-size: 24px;"
tabindex="0"
@@ -141,6 +144,7 @@ exports[`<PinnedEventTile /> should render pinned event with thread info 1`] = `
</div>
<div
class="mx_MTextBody mx_EventTile_content"
id="«r9»"
>
<div
class="mx_EventTile_body translate"
@@ -183,7 +187,7 @@ exports[`<PinnedEventTile /> should render pinned event with thread info 1`] = `
exports[`<PinnedEventTile /> should render the menu with all the options 1`] = `
<div
aria-label="Open menu"
aria-labelledby="radix-«r10»"
aria-labelledby="radix-«r14»"
aria-orientation="vertical"
class="_menu_1glhz_8"
data-align="start"
@@ -192,7 +196,7 @@ exports[`<PinnedEventTile /> should render the menu with all the options 1`] = `
data-side="right"
data-state="open"
dir="ltr"
id="radix-«r11»"
id="radix-«r15»"
role="menu"
style="outline: none; --radix-dropdown-menu-content-transform-origin: var(--radix-popper-transform-origin); --radix-dropdown-menu-content-available-width: var(--radix-popper-available-width); --radix-dropdown-menu-content-available-height: var(--radix-popper-available-height); --radix-dropdown-menu-trigger-width: var(--radix-popper-anchor-width); --radix-dropdown-menu-trigger-height: var(--radix-popper-anchor-height); pointer-events: auto;"
tabindex="-1"
@@ -374,7 +378,7 @@ exports[`<PinnedEventTile /> should render the menu with all the options 1`] = `
exports[`<PinnedEventTile /> should render the menu without unpin and delete 1`] = `
<div
aria-label="Open menu"
aria-labelledby="radix-«rl»"
aria-labelledby="radix-«ro»"
aria-orientation="vertical"
class="_menu_1glhz_8"
data-align="start"
@@ -383,7 +387,7 @@ exports[`<PinnedEventTile /> should render the menu without unpin and delete 1`]
data-side="right"
data-state="open"
dir="ltr"
id="radix-«rm»"
id="radix-«rp»"
role="menu"
style="outline: none; --radix-dropdown-menu-content-transform-origin: var(--radix-popper-transform-origin); --radix-dropdown-menu-content-available-width: var(--radix-popper-available-width); --radix-dropdown-menu-content-available-height: var(--radix-popper-available-height); --radix-dropdown-menu-trigger-width: var(--radix-popper-anchor-width); --radix-dropdown-menu-trigger-height: var(--radix-popper-anchor-height); pointer-events: auto;"
tabindex="-1"

View File

@@ -3,18 +3,21 @@
exports[`<PinnedMessageBanner /> should display display a poll event 1`] = `
<DocumentFragment>
<div
aria-label="This room has pinned messages. Click to view them."
aria-label="Pinned messages"
class="mx_PinnedMessageBanner"
data-single-message="true"
data-testid="pinned-message-banner"
role="region"
>
<button
aria-label="View the pinned message in the timeline."
aria-describedby="«ra»"
aria-label="View the pinned message in the timeline and the newest pinned message here"
class="mx_PinnedMessageBanner_main"
type="button"
>
<div
class="mx_PinnedMessageBanner_content"
id="«ra»"
>
<div
class="mx_PinnedMessageBanner_Indicators"
@@ -58,18 +61,21 @@ exports[`<PinnedMessageBanner /> should display display a poll event 1`] = `
exports[`<PinnedMessageBanner /> should display the last message when the pinned event array changed 1`] = `
<DocumentFragment>
<div
aria-label="This room has pinned messages. Click to view them."
aria-label="Pinned messages"
class="mx_PinnedMessageBanner"
data-single-message="false"
data-testid="pinned-message-banner"
role="region"
>
<button
aria-label="View the pinned message in the timeline."
aria-describedby="«r4»"
aria-label="View the pinned message in the timeline and the newest pinned message here"
class="mx_PinnedMessageBanner_main"
type="button"
>
<div
class="mx_PinnedMessageBanner_content"
id="«r4»"
>
<div
class="mx_PinnedMessageBanner_Indicators"
@@ -137,18 +143,21 @@ exports[`<PinnedMessageBanner /> should display the last message when the pinned
exports[`<PinnedMessageBanner /> should display the m.audio event type 1`] = `
<DocumentFragment>
<div
aria-label="This room has pinned messages. Click to view them."
aria-label="Pinned messages"
class="mx_PinnedMessageBanner"
data-single-message="true"
data-testid="pinned-message-banner"
role="region"
>
<button
aria-label="View the pinned message in the timeline."
aria-describedby="«r7»"
aria-label="View the pinned message in the timeline and the newest pinned message here"
class="mx_PinnedMessageBanner_main"
type="button"
>
<div
class="mx_PinnedMessageBanner_content"
id="«r7»"
>
<div
class="mx_PinnedMessageBanner_Indicators"
@@ -192,18 +201,21 @@ exports[`<PinnedMessageBanner /> should display the m.audio event type 1`] = `
exports[`<PinnedMessageBanner /> should display the m.file event type 1`] = `
<DocumentFragment>
<div
aria-label="This room has pinned messages. Click to view them."
aria-label="Pinned messages"
class="mx_PinnedMessageBanner"
data-single-message="true"
data-testid="pinned-message-banner"
role="region"
>
<button
aria-label="View the pinned message in the timeline."
aria-describedby="«r6»"
aria-label="View the pinned message in the timeline and the newest pinned message here"
class="mx_PinnedMessageBanner_main"
type="button"
>
<div
class="mx_PinnedMessageBanner_content"
id="«r6»"
>
<div
class="mx_PinnedMessageBanner_Indicators"
@@ -247,18 +259,21 @@ exports[`<PinnedMessageBanner /> should display the m.file event type 1`] = `
exports[`<PinnedMessageBanner /> should display the m.image event type 1`] = `
<DocumentFragment>
<div
aria-label="This room has pinned messages. Click to view them."
aria-label="Pinned messages"
class="mx_PinnedMessageBanner"
data-single-message="true"
data-testid="pinned-message-banner"
role="region"
>
<button
aria-label="View the pinned message in the timeline."
aria-describedby="«r9»"
aria-label="View the pinned message in the timeline and the newest pinned message here"
class="mx_PinnedMessageBanner_main"
type="button"
>
<div
class="mx_PinnedMessageBanner_content"
id="«r9»"
>
<div
class="mx_PinnedMessageBanner_Indicators"
@@ -302,18 +317,21 @@ exports[`<PinnedMessageBanner /> should display the m.image event type 1`] = `
exports[`<PinnedMessageBanner /> should display the m.video event type 1`] = `
<DocumentFragment>
<div
aria-label="This room has pinned messages. Click to view them."
aria-label="Pinned messages"
class="mx_PinnedMessageBanner"
data-single-message="true"
data-testid="pinned-message-banner"
role="region"
>
<button
aria-label="View the pinned message in the timeline."
aria-describedby="«r8»"
aria-label="View the pinned message in the timeline and the newest pinned message here"
class="mx_PinnedMessageBanner_main"
type="button"
>
<div
class="mx_PinnedMessageBanner_content"
id="«r8»"
>
<div
class="mx_PinnedMessageBanner_Indicators"
@@ -357,18 +375,21 @@ exports[`<PinnedMessageBanner /> should display the m.video event type 1`] = `
exports[`<PinnedMessageBanner /> should render 2 pinned event 1`] = `
<DocumentFragment>
<div
aria-label="This room has pinned messages. Click to view them."
aria-label="Pinned messages"
class="mx_PinnedMessageBanner"
data-single-message="false"
data-testid="pinned-message-banner"
role="region"
>
<button
aria-label="View the pinned message in the timeline."
aria-describedby="«r2»"
aria-label="View the pinned message in the timeline and the newest pinned message here"
class="mx_PinnedMessageBanner_main"
type="button"
>
<div
class="mx_PinnedMessageBanner_content"
id="«r2»"
>
<div
class="mx_PinnedMessageBanner_Indicators"
@@ -432,18 +453,21 @@ exports[`<PinnedMessageBanner /> should render 2 pinned event 1`] = `
exports[`<PinnedMessageBanner /> should render 4 pinned event 1`] = `
<DocumentFragment>
<div
aria-label="This room has pinned messages. Click to view them."
aria-label="Pinned messages"
class="mx_PinnedMessageBanner"
data-single-message="false"
data-testid="pinned-message-banner"
role="region"
>
<button
aria-label="View the pinned message in the timeline."
aria-describedby="«r3»"
aria-label="View the pinned message in the timeline and the newest pinned message here"
class="mx_PinnedMessageBanner_main"
type="button"
>
<div
class="mx_PinnedMessageBanner_content"
id="«r3»"
>
<div
class="mx_PinnedMessageBanner_Indicators"
@@ -511,18 +535,21 @@ exports[`<PinnedMessageBanner /> should render 4 pinned event 1`] = `
exports[`<PinnedMessageBanner /> should render a single pinned event 1`] = `
<DocumentFragment>
<div
aria-label="This room has pinned messages. Click to view them."
aria-label="Pinned messages"
class="mx_PinnedMessageBanner"
data-single-message="true"
data-testid="pinned-message-banner"
role="region"
>
<button
aria-label="View the pinned message in the timeline."
aria-describedby="«r1»"
aria-label="View the pinned message in the timeline and the newest pinned message here"
class="mx_PinnedMessageBanner_main"
type="button"
>
<div
class="mx_PinnedMessageBanner_content"
id="«r1»"
>
<div
class="mx_PinnedMessageBanner_Indicators"

View File

@@ -97,6 +97,7 @@ exports[`<LayoutSwitcher /> should render 1`] = `
</div>
<div
class="mx_EventTile_line"
id="4"
>
<div
class="mx_MTextBody mx_EventTile_content"
@@ -223,6 +224,7 @@ exports[`<LayoutSwitcher /> should render 1`] = `
</div>
<div
class="mx_EventTile_line"
id="5"
>
<div
class="mx_MTextBody mx_EventTile_content"
@@ -349,6 +351,7 @@ exports[`<LayoutSwitcher /> should render 1`] = `
</div>
<div
class="mx_EventTile_line"
id="6"
>
<div
class="mx_MTextBody mx_EventTile_content"

View File

@@ -240,6 +240,7 @@ exports[`AppearanceUserSettingsTab should render 1`] = `
</div>
<div
class="mx_EventTile_line"
id="1"
>
<div
class="mx_MTextBody mx_EventTile_content"
@@ -366,6 +367,7 @@ exports[`AppearanceUserSettingsTab should render 1`] = `
</div>
<div
class="mx_EventTile_line"
id="2"
>
<div
class="mx_MTextBody mx_EventTile_content"
@@ -492,6 +494,7 @@ exports[`AppearanceUserSettingsTab should render 1`] = `
</div>
<div
class="mx_EventTile_line"
id="3"
>
<div
class="mx_MTextBody mx_EventTile_content"