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

@@ -13,7 +13,7 @@
padding: var(--cpd-space-1x) var(--cpd-space-3x) var(--cpd-space-1x) var(--cpd-space-1x);
font: var(--cpd-font-body-xs-medium);
background-color: var(--cpd-color-alpha-gray-200);
background-color: var(--cpd-color-bg-subtle-secondary);
color: var(--cpd-color-text-secondary);
border-radius: 99px;

View File

@@ -48,4 +48,9 @@ export interface IBodyProps {
// Set to `true` to disable interactions (e.g. video controls) and to remove controls from the tab order.
// This may be useful when displaying a preview of the event.
inhibitInteraction?: boolean;
/**
* Optional ID for the root element.
*/
id?: string;
}

View File

@@ -51,6 +51,11 @@ interface IProps extends Omit<IBodyProps, "onMessageAllowed" | "mediaEventHelper
getRelationsForEvent?: GetRelationsForEvent;
isSeeingThroughMessageHiddenForModeration?: boolean;
/**
* Optional ID for the root element.
*/
id?: string;
}
export interface IOperableEventTile {
@@ -308,6 +313,7 @@ export default class MessageEvent extends React.Component<IProps> implements IMe
getRelationsForEvent: this.props.getRelationsForEvent,
isSeeingThroughMessageHiddenForModeration: this.props.isSeeingThroughMessageHiddenForModeration,
inhibitInteraction: this.props.inhibitInteraction,
id: this.props.id,
};
if (hasCaption) {
return <CaptionBody {...bodyProps} WrappedBodyType={BodyType} />;

View File

@@ -5,7 +5,7 @@
* Please see LICENSE files in the repository root for full details.
*/
import React, { type JSX } from "react";
import React, { type HTMLProps, type JSX } from "react";
import PinIcon from "@vector-im/compound-design-tokens/assets/web/icons/pin-solid";
import { _t } from "../../../languageHandler";
@@ -13,9 +13,9 @@ import { _t } from "../../../languageHandler";
/**
* A badge to indicate that a message is pinned.
*/
export function PinnedMessageBadge(): JSX.Element {
export function PinnedMessageBadge(props: Readonly<HTMLProps<HTMLDivElement>>): JSX.Element {
return (
<div className="mx_PinnedMessageBadge">
<div {...props} className="mx_PinnedMessageBadge">
<PinIcon width="16px" height="16px" />
{_t("room|pinned_message_badge")}
</div>

View File

@@ -384,7 +384,12 @@ export default class TextualBody extends React.Component<IBodyProps, IState> {
if (isEmote) {
return (
<div className="mx_MEmoteBody mx_EventTile_content" onClick={this.onBodyLinkClick} dir="auto">
<div
id={this.props.id}
className="mx_MEmoteBody mx_EventTile_content"
onClick={this.onBodyLinkClick}
dir="auto"
>
*&nbsp;
<span className="mx_MEmoteBody_sender" onClick={this.onEmoteSenderClick}>
{mxEvent.sender ? mxEvent.sender.name : mxEvent.getSender()}
@@ -397,7 +402,7 @@ export default class TextualBody extends React.Component<IBodyProps, IState> {
}
if (isNotice) {
return (
<div className="mx_MNoticeBody mx_EventTile_content" onClick={this.onBodyLinkClick}>
<div id={this.props.id} className="mx_MNoticeBody mx_EventTile_content" onClick={this.onBodyLinkClick}>
{body}
{widgets}
</div>
@@ -405,14 +410,14 @@ export default class TextualBody extends React.Component<IBodyProps, IState> {
}
if (isCaption) {
return (
<div className="mx_MTextBody mx_EventTile_caption" onClick={this.onBodyLinkClick}>
<div id={this.props.id} className="mx_MTextBody mx_EventTile_caption" onClick={this.onBodyLinkClick}>
{body}
{widgets}
</div>
);
}
return (
<div className="mx_MTextBody mx_EventTile_content" onClick={this.onBodyLinkClick}>
<div id={this.props.id} className="mx_MTextBody mx_EventTile_content" onClick={this.onBodyLinkClick}>
{body}
{widgets}
</div>

View File

@@ -34,6 +34,7 @@ import {
type UserVerificationStatus,
} from "matrix-js-sdk/src/crypto-api";
import { Tooltip } from "@vector-im/compound-web";
import { uniqueId } from "lodash";
import ReplyChain from "../elements/ReplyChain";
import { _t } from "../../../languageHandler";
@@ -918,6 +919,8 @@ export class UnwrappedEventTile extends React.Component<EventTileProps, IState>
public render(): ReactNode {
const msgtype = this.props.mxEvent.getContent().msgtype;
const eventType = this.props.mxEvent.getType();
const id = uniqueId();
const {
hasRenderer,
isBubbleMessage,
@@ -1142,7 +1145,7 @@ export class UnwrappedEventTile extends React.Component<EventTileProps, IState>
let pinnedMessageBadge: JSX.Element | undefined;
if (PinningUtils.isPinned(MatrixClientPeg.safeGet(), this.props.mxEvent)) {
pinnedMessageBadge = <PinnedMessageBadge />;
pinnedMessageBadge = <PinnedMessageBadge aria-describedby={id} tabIndex={0} />;
}
let reactionsRow: JSX.Element | undefined;
@@ -1237,7 +1240,7 @@ export class UnwrappedEventTile extends React.Component<EventTileProps, IState>
{avatar}
{sender}
</div>,
<div className={lineClasses} key="mx_EventTile_line" onContextMenu={this.onContextMenu}>
<div id={id} className={lineClasses} key="mx_EventTile_line" onContextMenu={this.onContextMenu}>
{this.renderContextMenu()}
{replyChain}
{renderTile(TimelineRenderingType.Thread, {
@@ -1425,7 +1428,7 @@ export class UnwrappedEventTile extends React.Component<EventTileProps, IState>
{sender}
{ircPadlock}
{avatar}
<div className={lineClasses} key="mx_EventTile_line" onContextMenu={this.onContextMenu}>
<div id={id} className={lineClasses} key="mx_EventTile_line" onContextMenu={this.onContextMenu}>
{this.renderContextMenu()}
{groupTimestamp}
{groupPadlock}

View File

@@ -7,7 +7,7 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Com
Please see LICENSE files in the repository root for full details.
*/
import React, { type JSX, useCallback, useState } from "react";
import React, { type JSX, useCallback, useId, useState } from "react";
import { EventTimeline, EventType, type MatrixEvent, type Room } from "matrix-js-sdk/src/matrix";
import { IconButton, Menu, MenuItem, Separator, Tooltip } from "@vector-im/compound-web";
import ViewIcon from "@vector-im/compound-design-tokens/assets/web/icons/visibility-on";
@@ -67,6 +67,7 @@ export function PinnedEventTile({ event, room, permalinkCreator }: PinnedEventTi
const isInThread = Boolean(event.threadRootId);
const displayThreadInfo = !event.isThreadRoot && isInThread;
const id = useId();
return (
<div className="mx_PinnedEventTile" role="listitem">
@@ -85,9 +86,10 @@ export function PinnedEventTile({ event, room, permalinkCreator }: PinnedEventTi
{event.sender?.name || sender}
</span>
</Tooltip>
<PinMenu event={event} room={room} permalinkCreator={permalinkCreator} />
<PinMenu event={event} room={room} permalinkCreator={permalinkCreator} contentId={id} />
</div>
<MessageEvent
id={id}
mxEvent={event}
maxImageHeight={150}
permalinkCreator={permalinkCreator}
@@ -131,12 +133,17 @@ export function PinnedEventTile({ event, room, permalinkCreator }: PinnedEventTi
/**
* Properties for {@link PinMenu}.
*/
interface PinMenuProps extends PinnedEventTileProps {}
interface PinMenuProps extends PinnedEventTileProps {
/**
* HTML ID of the pinned message content.
*/
contentId: string;
}
/**
* A popover menu with actions on the pinned event
*/
function PinMenu({ event, room, permalinkCreator }: PinMenuProps): JSX.Element {
function PinMenu({ event, room, permalinkCreator, contentId }: PinMenuProps): JSX.Element {
const [open, setOpen] = useState(false);
const matrixClient = useMatrixClientContext();
@@ -217,7 +224,11 @@ function PinMenu({ event, room, permalinkCreator }: PinMenuProps): JSX.Element {
side="right"
align="start"
trigger={
<IconButton size="24px" aria-label={_t("right_panel|pinned_messages|menu")}>
<IconButton
size="24px"
aria-label={_t("right_panel|pinned_messages|menu")}
aria-describedby={contentId}
>
<TriggerIcon />
</IconButton>
}

View File

@@ -6,7 +6,7 @@
* Please see LICENSE files in the repository root for full details.
*/
import React, { type JSX, useEffect, useRef, useState } from "react";
import React, { type JSX, useEffect, useId, useRef, useState } from "react";
import PinIcon from "@vector-im/compound-design-tokens/assets/web/icons/pin-solid";
import { Button } from "@vector-im/compound-web";
import { type MatrixEvent, type Room } from "matrix-js-sdk/src/matrix";
@@ -64,9 +64,13 @@ export function PinnedMessageBanner({
setCurrentEventIndex(() => eventCount - 1);
}, [eventCount]);
const isLastMessage = currentEventIndex === eventCount - 1;
const pinnedEvent = pinnedEvents[currentEventIndex];
useNotifyTimeline(pinnedEvent, resizeNotifier);
const id = useId();
if (!pinnedEvent) return null;
const shouldUseMessageEvent = pinnedEvent.isRedacted() || pinnedEvent.isDecryptionFailure();
@@ -90,18 +94,24 @@ export function PinnedMessageBanner({
return (
<div
role="region"
className="mx_PinnedMessageBanner"
data-single-message={isSinglePinnedEvent}
aria-label={_t("room|pinned_message_banner|description")}
data-testid="pinned-message-banner"
>
<button
aria-label={_t("room|pinned_message_banner|go_to_message")}
aria-label={
isLastMessage
? _t("room|pinned_message_banner|go_to_newest_message")
: _t("room|pinned_message_banner|go_to_next_message")
}
aria-describedby={id}
type="button"
className="mx_PinnedMessageBanner_main"
onClick={onBannerClick}
>
<div className="mx_PinnedMessageBanner_content">
<div className="mx_PinnedMessageBanner_content" id={id}>
<Indicators count={eventCount} currentIndex={currentEventIndex} />
<PinIcon width="20px" height="20px" className="mx_PinnedMessageBanner_PinIcon" />
{!isSinglePinnedEvent && (

View File

@@ -2069,8 +2069,9 @@
"pinned_message_banner": {
"button_close_list": "Close list",
"button_view_all": "View all",
"description": "This room has pinned messages. Click to view them.",
"go_to_message": "View the pinned message in the timeline.",
"description": "Pinned messages",
"go_to_newest_message": "View the pinned message in the timeline and the newest pinned message here",
"go_to_next_message": "View the pinned message in the timeline and the next oldest pinned message here",
"title": "<bold>%(index)s of %(length)s</bold> Pinned messages"
},
"read_topic": "Click to read topic",

View File

@@ -59,7 +59,7 @@ exports[`RoomView for a local room in state CREATING should match the snapshot 1
style="--cpd-icon-button-size: 100%;"
>
<svg
aria-labelledby="«rh2»"
aria-labelledby="«rh5»"
fill="currentColor"
height="1em"
viewBox="0 0 24 24"
@@ -75,7 +75,7 @@ exports[`RoomView for a local room in state CREATING should match the snapshot 1
<button
aria-disabled="false"
aria-label="Voice call"
aria-labelledby="«rh7»"
aria-labelledby="«rha»"
class="_icon-button_1pz9o_8"
data-kind="primary"
role="button"
@@ -101,7 +101,7 @@ exports[`RoomView for a local room in state CREATING should match the snapshot 1
</button>
<button
aria-label="Threads"
aria-labelledby="«rhc»"
aria-labelledby="«rhf»"
class="_icon-button_1pz9o_8"
data-kind="primary"
role="button"
@@ -128,7 +128,7 @@ exports[`RoomView for a local room in state CREATING should match the snapshot 1
</button>
<button
aria-label="Room info"
aria-labelledby="«rhh»"
aria-labelledby="«rhk»"
class="_icon-button_1pz9o_8"
data-kind="primary"
role="button"
@@ -158,7 +158,7 @@ exports[`RoomView for a local room in state CREATING should match the snapshot 1
>
<div
aria-label="2 members"
aria-labelledby="«rhm»"
aria-labelledby="«rhp»"
class="mx_AccessibleButton mx_FacePile"
role="button"
tabindex="0"
@@ -278,7 +278,7 @@ exports[`RoomView for a local room in state ERROR should match the snapshot 1`]
style="--cpd-icon-button-size: 100%;"
>
<svg
aria-labelledby="«ri0»"
aria-labelledby="«ri3»"
fill="currentColor"
height="1em"
viewBox="0 0 24 24"
@@ -294,7 +294,7 @@ exports[`RoomView for a local room in state ERROR should match the snapshot 1`]
<button
aria-disabled="false"
aria-label="Voice call"
aria-labelledby="«ri5»"
aria-labelledby="«ri8»"
class="_icon-button_1pz9o_8"
data-kind="primary"
role="button"
@@ -320,7 +320,7 @@ exports[`RoomView for a local room in state ERROR should match the snapshot 1`]
</button>
<button
aria-label="Threads"
aria-labelledby="«ria»"
aria-labelledby="«rid»"
class="_icon-button_1pz9o_8"
data-kind="primary"
role="button"
@@ -347,7 +347,7 @@ exports[`RoomView for a local room in state ERROR should match the snapshot 1`]
</button>
<button
aria-label="Room info"
aria-labelledby="«rif»"
aria-labelledby="«rii»"
class="_icon-button_1pz9o_8"
data-kind="primary"
role="button"
@@ -377,7 +377,7 @@ exports[`RoomView for a local room in state ERROR should match the snapshot 1`]
>
<div
aria-label="2 members"
aria-labelledby="«rik»"
aria-labelledby="«rin»"
class="mx_AccessibleButton mx_FacePile"
role="button"
tabindex="0"
@@ -583,7 +583,7 @@ exports[`RoomView for a local room in state NEW should match the snapshot 1`] =
style="--cpd-icon-button-size: 100%;"
>
<svg
aria-labelledby="«rca»"
aria-labelledby="«rcd»"
fill="currentColor"
height="1em"
viewBox="0 0 24 24"
@@ -599,7 +599,7 @@ exports[`RoomView for a local room in state NEW should match the snapshot 1`] =
<button
aria-disabled="false"
aria-label="Voice call"
aria-labelledby="«rcf»"
aria-labelledby="«rci»"
class="_icon-button_1pz9o_8"
data-kind="primary"
role="button"
@@ -625,7 +625,7 @@ exports[`RoomView for a local room in state NEW should match the snapshot 1`] =
</button>
<button
aria-label="Threads"
aria-labelledby="«rck»"
aria-labelledby="«rcn»"
class="_icon-button_1pz9o_8"
data-kind="primary"
role="button"
@@ -652,7 +652,7 @@ exports[`RoomView for a local room in state NEW should match the snapshot 1`] =
</button>
<button
aria-label="Room info"
aria-labelledby="«rcp»"
aria-labelledby="«rcs»"
class="_icon-button_1pz9o_8"
data-kind="primary"
role="button"
@@ -682,7 +682,7 @@ exports[`RoomView for a local room in state NEW should match the snapshot 1`] =
>
<div
aria-label="2 members"
aria-labelledby="«rcu»"
aria-labelledby="«rd1»"
class="mx_AccessibleButton mx_FacePile"
role="button"
tabindex="0"
@@ -801,7 +801,7 @@ exports[`RoomView for a local room in state NEW should match the snapshot 1`] =
>
<svg
aria-label="Messages in this room are not end-to-end encrypted"
aria-labelledby="«rd7»"
aria-labelledby="«rda»"
class="mx_E2EIcon mx_MessageComposer_e2eIcon"
color="var(--cpd-color-icon-info-primary)"
fill="currentColor"
@@ -984,7 +984,7 @@ exports[`RoomView for a local room in state NEW that is encrypted should match t
style="--cpd-icon-button-size: 100%;"
>
<svg
aria-labelledby="«rem»"
aria-labelledby="«rep»"
fill="currentColor"
height="1em"
viewBox="0 0 24 24"
@@ -1000,7 +1000,7 @@ exports[`RoomView for a local room in state NEW that is encrypted should match t
<button
aria-disabled="false"
aria-label="Voice call"
aria-labelledby="«rer»"
aria-labelledby="«reu»"
class="_icon-button_1pz9o_8"
data-kind="primary"
role="button"
@@ -1026,7 +1026,7 @@ exports[`RoomView for a local room in state NEW that is encrypted should match t
</button>
<button
aria-label="Threads"
aria-labelledby="«rf0»"
aria-labelledby="«rf3»"
class="_icon-button_1pz9o_8"
data-kind="primary"
role="button"
@@ -1053,7 +1053,7 @@ exports[`RoomView for a local room in state NEW that is encrypted should match t
</button>
<button
aria-label="Room info"
aria-labelledby="«rf5»"
aria-labelledby="«rf8»"
class="_icon-button_1pz9o_8"
data-kind="primary"
role="button"
@@ -1083,7 +1083,7 @@ exports[`RoomView for a local room in state NEW that is encrypted should match t
>
<div
aria-label="2 members"
aria-labelledby="«rfa»"
aria-labelledby="«rfd»"
class="mx_AccessibleButton mx_FacePile"
role="button"
tabindex="0"
@@ -1197,7 +1197,7 @@ exports[`RoomView for a local room in state NEW that is encrypted should match t
>
<svg
aria-label="Messages in this room are not end-to-end encrypted"
aria-labelledby="«rfj»"
aria-labelledby="«rfm»"
class="mx_E2EIcon mx_MessageComposer_e2eIcon"
color="var(--cpd-color-icon-info-primary)"
fill="currentColor"
@@ -1466,7 +1466,7 @@ exports[`RoomView should not display the timeline when the room encryption is lo
style="--cpd-icon-button-size: 100%;"
>
<svg
aria-labelledby="«r2i»"
aria-labelledby="«r2j»"
fill="currentColor"
height="1em"
viewBox="0 0 24 24"
@@ -1482,7 +1482,7 @@ exports[`RoomView should not display the timeline when the room encryption is lo
<button
aria-disabled="false"
aria-label="Voice call"
aria-labelledby="«r2n»"
aria-labelledby="«r2o»"
class="_icon-button_1pz9o_8"
data-kind="primary"
role="button"
@@ -1508,7 +1508,7 @@ exports[`RoomView should not display the timeline when the room encryption is lo
</button>
<button
aria-label="Threads"
aria-labelledby="«r2s»"
aria-labelledby="«r2t»"
class="_icon-button_1pz9o_8"
data-kind="primary"
role="button"
@@ -1535,7 +1535,7 @@ exports[`RoomView should not display the timeline when the room encryption is lo
</button>
<button
aria-label="Room info"
aria-labelledby="«r31»"
aria-labelledby="«r32»"
class="_icon-button_1pz9o_8"
data-kind="primary"
role="button"
@@ -1565,7 +1565,7 @@ exports[`RoomView should not display the timeline when the room encryption is lo
>
<div
aria-label="0 members"
aria-labelledby="«r36»"
aria-labelledby="«r37»"
class="mx_AccessibleButton mx_FacePile"
role="button"
tabindex="0"
@@ -1678,7 +1678,7 @@ exports[`RoomView should not display the timeline when the room encryption is lo
style="--cpd-icon-button-size: 100%;"
>
<svg
aria-labelledby="«r2i»"
aria-labelledby="«r2j»"
fill="currentColor"
height="1em"
viewBox="0 0 24 24"
@@ -1694,7 +1694,7 @@ exports[`RoomView should not display the timeline when the room encryption is lo
<button
aria-disabled="false"
aria-label="Voice call"
aria-labelledby="«r2n»"
aria-labelledby="«r2o»"
class="_icon-button_1pz9o_8"
data-kind="primary"
role="button"
@@ -1720,7 +1720,7 @@ exports[`RoomView should not display the timeline when the room encryption is lo
</button>
<button
aria-label="Threads"
aria-labelledby="«r2s»"
aria-labelledby="«r2t»"
class="_icon-button_1pz9o_8"
data-kind="primary"
role="button"
@@ -1747,7 +1747,7 @@ exports[`RoomView should not display the timeline when the room encryption is lo
</button>
<button
aria-label="Room info"
aria-labelledby="«r31»"
aria-labelledby="«r32»"
class="_icon-button_1pz9o_8"
data-kind="primary"
role="button"
@@ -1777,7 +1777,7 @@ exports[`RoomView should not display the timeline when the room encryption is lo
>
<div
aria-label="0 members"
aria-labelledby="«r36»"
aria-labelledby="«r37»"
class="mx_AccessibleButton mx_FacePile"
role="button"
tabindex="0"
@@ -1845,7 +1845,7 @@ exports[`RoomView should not display the timeline when the room encryption is lo
tabindex="0"
>
<div
aria-labelledby="«r3k»"
aria-labelledby="«r3m»"
class="mx_E2EIcon mx_E2EIcon_verified mx_MessageComposer_e2eIcon"
data-testid="e2e-icon"
>
@@ -2056,7 +2056,7 @@ exports[`RoomView video rooms should render joined video room view 1`] = `
</button>
<button
aria-label="Chat"
aria-labelledby="«r7i»"
aria-labelledby="«r7l»"
class="_icon-button_1pz9o_8"
data-kind="primary"
role="button"
@@ -2083,7 +2083,7 @@ exports[`RoomView video rooms should render joined video room view 1`] = `
</button>
<button
aria-label="Threads"
aria-labelledby="«r7n»"
aria-labelledby="«r7q»"
class="_icon-button_1pz9o_8"
data-kind="primary"
role="button"
@@ -2110,7 +2110,7 @@ exports[`RoomView video rooms should render joined video room view 1`] = `
</button>
<button
aria-label="Room info"
aria-labelledby="«r7s»"
aria-labelledby="«r7v»"
class="_icon-button_1pz9o_8"
data-kind="primary"
role="button"
@@ -2140,7 +2140,7 @@ exports[`RoomView video rooms should render joined video room view 1`] = `
>
<div
aria-label="0 members"
aria-labelledby="«r81»"
aria-labelledby="«r84»"
class="mx_AccessibleButton mx_FacePile"
role="button"
tabindex="0"
@@ -2216,7 +2216,7 @@ exports[`RoomView video rooms should render joined video room view 1`] = `
</p>
</div>
<button
aria-labelledby="«r8a»"
aria-labelledby="«r8d»"
class="_icon-button_1pz9o_8"
data-kind="secondary"
data-testid="base-card-close-button"
@@ -2276,7 +2276,7 @@ exports[`RoomView video rooms should render joined video room view 1`] = `
>
<svg
aria-label="Messages in this room are not end-to-end encrypted"
aria-labelledby="«r8j»"
aria-labelledby="«r8m»"
class="mx_E2EIcon mx_MessageComposer_e2eIcon"
color="var(--cpd-color-icon-info-primary)"
fill="currentColor"

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"

File diff suppressed because one or more lines are too long