Fix alignment of RTL messages (#12837)

* Fix alignment of RTL messages

Inspired by https://github.com/matrix-org/matrix-react-sdk/pull/5453 but
hopefully with the edited marker in the right place.

This is a PoC: types aren't correct and the style needs pulling
out to a class. Plus it would probably need more visual tests added.
If this looks acceptable, I can make these changes.

* Fix spacing between text and edited annotation

* Update snapshot

* Update more snapshots

* More snapshots

* More more snapshots

* Split out style

* Fix emotes

This will cause them always be right-justified if the display name
is rtl.

* Add playwright test for ltr/rtl message rendering

* Better snapshots

* Await on message sending

* Better waiting, hopefully

* Old snapshot files

* Really hopefully fixed screenshots this time

* Don't include the message action bar in the screenshots
This commit is contained in:
David Baker 2024-07-31 23:23:46 +01:00 committed by GitHub
parent f3ac6692da
commit a0c029c3c1
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
24 changed files with 242 additions and 79 deletions

View file

@ -0,0 +1,113 @@
/*
Copyright 2024 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
/* See readme.md for tips on writing these tests. */
import { Locator, Page } from "playwright-core";
import { test, expect } from "../../element-web-test";
async function sendMessage(page: Page, message: string): Promise<Locator> {
await page.getByRole("textbox", { name: "Send a message…" }).fill(message);
await page.getByRole("button", { name: "Send message" }).click();
const msgTile = await page.locator(".mx_EventTile_last");
await msgTile.locator(".mx_EventTile_receiptSent").waitFor();
return msgTile;
}
async function editMessage(page: Page, message: Locator, newMsg: string): Promise<void> {
const line = message.locator(".mx_EventTile_line");
await line.hover();
await line.getByRole("button", { name: "Edit" }).click();
const editComposer = page.getByRole("textbox", { name: "Edit message" });
await page.getByLabel("User menu").hover(); // Just to un-hover the message line
await editComposer.fill(newMsg);
await editComposer.press("Enter");
}
test.describe("Message rendering", () => {
[
{ direction: "ltr", displayName: "Quentin" },
{ direction: "rtl", displayName: "كوينتين" },
].forEach(({ direction, displayName }) => {
test.describe(`with ${direction} display name`, () => {
test.use({
displayName,
room: async ({ user, app }, use) => {
const roomId = await app.client.createRoom({ name: "Test room" });
await use({ roomId });
},
});
test("should render a basic LTR text message", async ({ page, user, app, room }) => {
await page.goto(`#/room/${room.roomId}`);
const msgTile = await sendMessage(page, "Hello, world!");
await expect(msgTile).toMatchScreenshot(`basic-message-ltr-${direction}displayname.png`, {
mask: [page.locator(".mx_MessageTimestamp")],
});
});
test("should render an LTR emote", async ({ page, user, app, room }) => {
await page.goto(`#/room/${room.roomId}`);
const msgTile = await sendMessage(page, "/me lays an egg");
await expect(msgTile).toMatchScreenshot(`emote-ltr-${direction}displayname.png`);
});
test("should render an edited LTR message", async ({ page, user, app, room }) => {
await page.goto(`#/room/${room.roomId}`);
const msgTile = await sendMessage(page, "Hello, world!");
await editMessage(page, msgTile, "Hello, universe!");
await expect(msgTile).toMatchScreenshot(`edited-message-ltr-${direction}displayname.png`, {
mask: [page.locator(".mx_MessageTimestamp")],
});
});
test("should render a basic RTL text message", async ({ page, user, app, room }) => {
await page.goto(`#/room/${room.roomId}`);
const msgTile = await sendMessage(page, "مرحبا بالعالم!");
await expect(msgTile).toMatchScreenshot(`basic-message-rtl-${direction}displayname.png`, {
mask: [page.locator(".mx_MessageTimestamp")],
});
});
test("should render an RTL emote", async ({ page, user, app, room }) => {
await page.goto(`#/room/${room.roomId}`);
const msgTile = await sendMessage(page, "/me يضع بيضة");
await expect(msgTile).toMatchScreenshot(`emote-rtl-${direction}displayname.png`);
});
test("should render an edited RTL message", async ({ page, user, app, room }) => {
await page.goto(`#/room/${room.roomId}`);
const msgTile = await sendMessage(page, "مرحبا بالعالم!");
await editMessage(page, msgTile, "مرحبا بالكون!");
await expect(msgTile).toMatchScreenshot(`edited-message-rtl-${direction}displayname.png`, {
mask: [page.locator(".mx_MessageTimestamp")],
});
});
});
});
});

Binary file not shown.

After

Width:  |  Height:  |  Size: 4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

View file

@ -16,6 +16,7 @@ limitations under the License.
.mx_MEmoteBody { .mx_MEmoteBody {
white-space: pre-wrap; white-space: pre-wrap;
text-align: start;
} }
.mx_MEmoteBody_sender { .mx_MEmoteBody_sender {

View file

@ -43,6 +43,7 @@ $left-gutter: 64px;
.mx_EventTile_body { .mx_EventTile_body {
overflow-y: hidden; overflow-y: hidden;
text-align: start;
} }
.mx_EventTile_receiptSent, .mx_EventTile_receiptSent,
@ -676,7 +677,7 @@ $left-gutter: 64px;
font-size: $font-12px; font-size: $font-12px;
color: $secondary-content; color: $secondary-content;
display: inline-block; display: inline-block;
margin-left: 9px; margin-inline-start: 9px;
} }
.mx_EventTile_edited { .mx_EventTile_edited {
@ -1443,6 +1444,10 @@ $left-gutter: 64px;
} }
} }
.mx_EventTile_annotated {
display: flex;
}
/* Media query for mobile UI */ /* Media query for mobile UI */
@media only screen and (max-width: 480px) { @media only screen and (max-width: 480px) {
.mx_EventTile_content { .mx_EventTile_content {

View file

@ -303,7 +303,6 @@ export interface EventRenderOpts {
disableBigEmoji?: boolean; disableBigEmoji?: boolean;
stripReplyFallback?: boolean; stripReplyFallback?: boolean;
forComposerQuote?: boolean; forComposerQuote?: boolean;
ref?: React.Ref<HTMLSpanElement>;
} }
function analyseEvent(content: IContent, highlights: Optional<string[]>, opts: EventRenderOpts = {}): EventAnalysis { function analyseEvent(content: IContent, highlights: Optional<string[]>, opts: EventRenderOpts = {}): EventAnalysis {
@ -375,7 +374,61 @@ function analyseEvent(content: IContent, highlights: Optional<string[]>, opts: E
} }
} }
export function bodyToNode(content: IContent, highlights: Optional<string[]>, opts: EventRenderOpts = {}): ReactNode { export function bodyToDiv(
content: IContent,
highlights: Optional<string[]>,
opts: EventRenderOpts = {},
ref?: React.Ref<HTMLDivElement>,
): ReactNode {
const { strippedBody, formattedBody, emojiBodyElements, className } = bodyToNode(content, highlights, opts);
return formattedBody ? (
<div
key="body"
ref={ref}
className={className}
dangerouslySetInnerHTML={{ __html: formattedBody }}
dir="auto"
/>
) : (
<div key="body" ref={ref} className={className} dir="auto">
{emojiBodyElements || strippedBody}
</div>
);
}
export function bodyToSpan(
content: IContent,
highlights: Optional<string[]>,
opts: EventRenderOpts = {},
ref?: React.Ref<HTMLSpanElement>,
includeDir = true,
): ReactNode {
const { strippedBody, formattedBody, emojiBodyElements, className } = bodyToNode(content, highlights, opts);
return formattedBody ? (
<span
key="body"
ref={ref}
className={className}
dangerouslySetInnerHTML={{ __html: formattedBody }}
dir={includeDir ? "auto" : undefined}
/>
) : (
<span key="body" ref={ref} className={className} dir={includeDir ? "auto" : undefined}>
{emojiBodyElements || strippedBody}
</span>
);
}
interface BodyToNodeReturn {
strippedBody: string;
formattedBody?: string;
emojiBodyElements: JSX.Element[] | undefined;
className: string;
}
function bodyToNode(content: IContent, highlights: Optional<string[]>, opts: EventRenderOpts = {}): BodyToNodeReturn {
const eventInfo = analyseEvent(content, highlights, opts); const eventInfo = analyseEvent(content, highlights, opts);
let emojiBody = false; let emojiBody = false;
@ -419,19 +472,7 @@ export function bodyToNode(content: IContent, highlights: Optional<string[]>, op
emojiBodyElements = formatEmojis(eventInfo.strippedBody, false) as JSX.Element[]; emojiBodyElements = formatEmojis(eventInfo.strippedBody, false) as JSX.Element[];
} }
return formattedBody ? ( return { strippedBody: eventInfo.strippedBody, formattedBody, emojiBodyElements, className };
<span
key="body"
ref={opts.ref}
className={className}
dangerouslySetInnerHTML={{ __html: formattedBody }}
dir="auto"
/>
) : (
<span key="body" ref={opts.ref} className={className} dir="auto">
{emojiBodyElements || eventInfo.strippedBody}
</span>
);
} }
/** /**

View file

@ -172,7 +172,7 @@ export default class EditHistoryMessage extends React.PureComponent<IProps, ISta
if (this.props.previousEdit) { if (this.props.previousEdit) {
contentElements = editBodyDiffToHtml(getReplacedContent(this.props.previousEdit), content); contentElements = editBodyDiffToHtml(getReplacedContent(this.props.previousEdit), content);
} else { } else {
contentElements = HtmlUtils.bodyToNode(content, null, { contentElements = HtmlUtils.bodyToSpan(content, null, {
stripReplyFallback: true, stripReplyFallback: true,
}); });
} }

View file

@ -59,7 +59,7 @@ interface IState {
} }
export default class TextualBody extends React.Component<IBodyProps, IState> { export default class TextualBody extends React.Component<IBodyProps, IState> {
private readonly contentRef = createRef<HTMLSpanElement>(); private readonly contentRef = createRef<HTMLDivElement>();
private unmounted = false; private unmounted = false;
private pills: Element[] = []; private pills: Element[] = [];
@ -566,34 +566,38 @@ export default class TextualBody extends React.Component<IBodyProps, IState> {
} }
const mxEvent = this.props.mxEvent; const mxEvent = this.props.mxEvent;
const content = mxEvent.getContent(); const content = mxEvent.getContent();
let isNotice = false; const isNotice = content.msgtype === MsgType.Notice;
let isEmote = false; const isEmote = content.msgtype === MsgType.Emote;
const willHaveWrapper =
this.props.replacingEventId || this.props.isSeeingThroughMessageHiddenForModeration || isEmote;
// only strip reply if this is the original replying event, edits thereafter do not have the fallback // only strip reply if this is the original replying event, edits thereafter do not have the fallback
const stripReply = !mxEvent.replacingEvent() && !!getParentEventId(mxEvent); const stripReply = !mxEvent.replacingEvent() && !!getParentEventId(mxEvent);
isEmote = content.msgtype === MsgType.Emote;
isNotice = content.msgtype === MsgType.Notice; const htmlOpts = {
let body = HtmlUtils.bodyToNode(content, this.props.highlights, {
disableBigEmoji: isEmote || !SettingsStore.getValue<boolean>("TextualBody.enableBigEmoji"), disableBigEmoji: isEmote || !SettingsStore.getValue<boolean>("TextualBody.enableBigEmoji"),
// Part of Replies fallback support // Part of Replies fallback support
stripReplyFallback: stripReply, stripReplyFallback: stripReply,
ref: this.contentRef, };
}); let body = willHaveWrapper
? HtmlUtils.bodyToSpan(content, this.props.highlights, htmlOpts, this.contentRef, false)
: HtmlUtils.bodyToDiv(content, this.props.highlights, htmlOpts, this.contentRef);
if (this.props.replacingEventId) { if (this.props.replacingEventId) {
body = ( body = (
<> <div dir="auto" className="mx_EventTile_annotated">
{body} {body}
{this.renderEditedMarker()} {this.renderEditedMarker()}
</> </div>
); );
} }
if (this.props.isSeeingThroughMessageHiddenForModeration) { if (this.props.isSeeingThroughMessageHiddenForModeration) {
body = ( body = (
<> <div dir="auto" className="mx_EventTile_annotated">
{body} {body}
{this.renderPendingModerationMarker()} {this.renderPendingModerationMarker()}
</> </div>
); );
} }
@ -624,7 +628,7 @@ export default class TextualBody extends React.Component<IBodyProps, IState> {
if (isEmote) { if (isEmote) {
return ( return (
<div className="mx_MEmoteBody mx_EventTile_content" onClick={this.onBodyLinkClick}> <div className="mx_MEmoteBody mx_EventTile_content" onClick={this.onBodyLinkClick} dir="auto">
*&nbsp; *&nbsp;
<span className="mx_MEmoteBody_sender" onClick={this.onEmoteSenderClick}> <span className="mx_MEmoteBody_sender" onClick={this.onEmoteSenderClick}>
{mxEvent.sender ? mxEvent.sender.name : mxEvent.getSender()} {mxEvent.sender ? mxEvent.sender.name : mxEvent.getSender()}

View file

@ -19,7 +19,7 @@ import { mocked } from "jest-mock";
import { render, screen } from "@testing-library/react"; import { render, screen } from "@testing-library/react";
import { IContent } from "matrix-js-sdk/src/matrix"; import { IContent } from "matrix-js-sdk/src/matrix";
import { bodyToNode, formatEmojis, topicToHtml } from "../src/HtmlUtils"; import { bodyToSpan, formatEmojis, topicToHtml } from "../src/HtmlUtils";
import SettingsStore from "../src/settings/SettingsStore"; import SettingsStore from "../src/settings/SettingsStore";
jest.mock("../src/settings/SettingsStore"); jest.mock("../src/settings/SettingsStore");
@ -66,7 +66,7 @@ describe("topicToHtml", () => {
describe("bodyToHtml", () => { describe("bodyToHtml", () => {
function getHtml(content: IContent, highlights?: string[]): string { function getHtml(content: IContent, highlights?: string[]): string {
return (bodyToNode(content, highlights, {}) as ReactElement).props.dangerouslySetInnerHTML.__html; return (bodyToSpan(content, highlights, {}) as ReactElement).props.dangerouslySetInnerHTML.__html;
} }
it("should apply highlights to HTML messages", () => { it("should apply highlights to HTML messages", () => {
@ -108,14 +108,14 @@ describe("bodyToHtml", () => {
}); });
it("generates big emoji for emoji made of multiple characters", () => { it("generates big emoji for emoji made of multiple characters", () => {
const { asFragment } = render(bodyToNode({ body: "👨‍👩‍👧‍👦 ↔️ 🇮🇸", msgtype: "m.text" }, [], {}) as ReactElement); const { asFragment } = render(bodyToSpan({ body: "👨‍👩‍👧‍👦 ↔️ 🇮🇸", msgtype: "m.text" }, [], {}) as ReactElement);
expect(asFragment()).toMatchSnapshot(); expect(asFragment()).toMatchSnapshot();
}); });
it("should generate big emoji for an emoji-only reply to a message", () => { it("should generate big emoji for an emoji-only reply to a message", () => {
const { asFragment } = render( const { asFragment } = render(
bodyToNode( bodyToSpan(
{ {
"body": "> <@sender1:server> Test\n\n🥰", "body": "> <@sender1:server> Test\n\n🥰",
"format": "org.matrix.custom.html", "format": "org.matrix.custom.html",
@ -139,7 +139,7 @@ describe("bodyToHtml", () => {
}); });
it("does not mistake characters in text presentation mode for emoji", () => { it("does not mistake characters in text presentation mode for emoji", () => {
const { asFragment } = render(bodyToNode({ body: "↔ ❗︎", msgtype: "m.text" }, [], {}) as ReactElement); const { asFragment } = render(bodyToSpan({ body: "↔ ❗︎", msgtype: "m.text" }, [], {}) as ReactElement);
expect(asFragment()).toMatchSnapshot(); expect(asFragment()).toMatchSnapshot();
}); });

View file

@ -1,7 +1,7 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP // Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`<TextualBody /> renders formatted m.text correctly italics, bold, underline and strikethrough render as expected 1`] = ` exports[`<TextualBody /> renders formatted m.text correctly italics, bold, underline and strikethrough render as expected 1`] = `
<span <div
class="mx_EventTile_body markdown-body translate" class="mx_EventTile_body markdown-body translate"
dir="auto" dir="auto"
> >
@ -21,11 +21,11 @@ exports[`<TextualBody /> renders formatted m.text correctly italics, bold, under
<u> <u>
u u
</u> </u>
</span> </div>
`; `;
exports[`<TextualBody /> renders formatted m.text correctly linkification is not applied to code blocks 1`] = ` exports[`<TextualBody /> renders formatted m.text correctly linkification is not applied to code blocks 1`] = `
<span <div
class="mx_EventTile_body markdown-body translate" class="mx_EventTile_body markdown-body translate"
dir="auto" dir="auto"
> >
@ -62,11 +62,11 @@ exports[`<TextualBody /> renders formatted m.text correctly linkification is not
</div> </div>
</span> </div>
`; `;
exports[`<TextualBody /> renders formatted m.text correctly pills appear for an MXID permalink 1`] = ` exports[`<TextualBody /> renders formatted m.text correctly pills appear for an MXID permalink 1`] = `
<span <div
class="mx_EventTile_body markdown-body translate" class="mx_EventTile_body markdown-body translate"
dir="auto" dir="auto"
> >
@ -105,7 +105,7 @@ exports[`<TextualBody /> renders formatted m.text correctly pills appear for an
</a> </a>
</bdi> </bdi>
</span> </span>
</span> </div>
`; `;
exports[`<TextualBody /> renders formatted m.text correctly pills appear for event permalinks without a custom label 1`] = ` exports[`<TextualBody /> renders formatted m.text correctly pills appear for event permalinks without a custom label 1`] = `
@ -113,7 +113,7 @@ exports[`<TextualBody /> renders formatted m.text correctly pills appear for eve
<div <div
class="mx_MTextBody mx_EventTile_content" class="mx_MTextBody mx_EventTile_content"
> >
<span <div
class="mx_EventTile_body markdown-body translate" class="mx_EventTile_body markdown-body translate"
dir="auto" dir="auto"
> >
@ -152,7 +152,7 @@ exports[`<TextualBody /> renders formatted m.text correctly pills appear for eve
</a> </a>
</bdi> </bdi>
</span> </span>
</span> </div>
</div> </div>
</DocumentFragment> </DocumentFragment>
`; `;
@ -162,7 +162,7 @@ exports[`<TextualBody /> renders formatted m.text correctly pills appear for roo
<div <div
class="mx_MTextBody mx_EventTile_content" class="mx_MTextBody mx_EventTile_content"
> >
<span <div
class="mx_EventTile_body markdown-body translate" class="mx_EventTile_body markdown-body translate"
dir="auto" dir="auto"
> >
@ -202,7 +202,7 @@ exports[`<TextualBody /> renders formatted m.text correctly pills appear for roo
</bdi> </bdi>
</span> </span>
with vias with vias
</span> </div>
</div> </div>
</DocumentFragment> </DocumentFragment>
`; `;
@ -212,7 +212,7 @@ exports[`<TextualBody /> renders formatted m.text correctly pills do not appear
<div <div
class="mx_MTextBody mx_EventTile_content" class="mx_MTextBody mx_EventTile_content"
> >
<span <div
class="mx_EventTile_body markdown-body translate" class="mx_EventTile_body markdown-body translate"
dir="auto" dir="auto"
> >
@ -224,13 +224,13 @@ exports[`<TextualBody /> renders formatted m.text correctly pills do not appear
event link event link
</a> </a>
with text with text
</span> </div>
</div> </div>
</DocumentFragment> </DocumentFragment>
`; `;
exports[`<TextualBody /> renders formatted m.text correctly pills do not appear in code blocks 1`] = ` exports[`<TextualBody /> renders formatted m.text correctly pills do not appear in code blocks 1`] = `
<span <div
class="mx_EventTile_body markdown-body translate" class="mx_EventTile_body markdown-body translate"
dir="auto" dir="auto"
> >
@ -266,11 +266,11 @@ exports[`<TextualBody /> renders formatted m.text correctly pills do not appear
</div> </div>
</span> </div>
`; `;
exports[`<TextualBody /> renders formatted m.text correctly pills get injected correctly into the DOM 1`] = ` exports[`<TextualBody /> renders formatted m.text correctly pills get injected correctly into the DOM 1`] = `
<span <div
class="mx_EventTile_body markdown-body translate" class="mx_EventTile_body markdown-body translate"
dir="auto" dir="auto"
> >
@ -309,20 +309,20 @@ exports[`<TextualBody /> renders formatted m.text correctly pills get injected c
</a> </a>
</bdi> </bdi>
</span> </span>
</span> </div>
`; `;
exports[`<TextualBody /> renders formatted m.text correctly renders formatted body without html correctly 1`] = ` exports[`<TextualBody /> renders formatted m.text correctly renders formatted body without html correctly 1`] = `
<span <div
class="mx_EventTile_body translate" class="mx_EventTile_body translate"
dir="auto" dir="auto"
> >
escaped *markdown* escaped *markdown*
</span> </div>
`; `;
exports[`<TextualBody /> renders formatted m.text correctly spoilers get injected properly into the DOM 1`] = ` exports[`<TextualBody /> renders formatted m.text correctly spoilers get injected properly into the DOM 1`] = `
<span <div
class="mx_EventTile_body markdown-body translate" class="mx_EventTile_body markdown-body translate"
dir="auto" dir="auto"
> >
@ -346,29 +346,28 @@ exports[`<TextualBody /> renders formatted m.text correctly spoilers get injecte
</span> </span>
</button> </button>
</span> </span>
</span> </div>
`; `;
exports[`<TextualBody /> renders m.emote correctly 1`] = ` exports[`<TextualBody /> renders m.emote correctly 1`] = `
<span <span
class="mx_EventTile_body translate" class="mx_EventTile_body translate"
dir="auto"
> >
winks winks
</span> </span>
`; `;
exports[`<TextualBody /> renders m.notice correctly 1`] = ` exports[`<TextualBody /> renders m.notice correctly 1`] = `
<span <div
class="mx_EventTile_body translate" class="mx_EventTile_body translate"
dir="auto" dir="auto"
> >
this is a notice, probably from a bot this is a notice, probably from a bot
</span> </div>
`; `;
exports[`<TextualBody /> renders plain-text m.text correctly linkification get applied correctly into the DOM 1`] = ` exports[`<TextualBody /> renders plain-text m.text correctly linkification get applied correctly into the DOM 1`] = `
<span <div
class="mx_EventTile_body translate" class="mx_EventTile_body translate"
dir="auto" dir="auto"
> >
@ -381,7 +380,7 @@ exports[`<TextualBody /> renders plain-text m.text correctly linkification get a
> >
https://matrix.org/ https://matrix.org/
</a> </a>
</span> </div>
`; `;
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="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>"`;
@ -389,7 +388,7 @@ exports[`<TextualBody /> renders plain-text m.text correctly should pillify a pe
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>"`; 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>"`;
exports[`<TextualBody /> renders plain-text m.text correctly should pillify a permalink to an unknown message in the same room with the label »Message« 1`] = ` exports[`<TextualBody /> renders plain-text m.text correctly should pillify a permalink to an unknown message in the same room with the label »Message« 1`] = `
<span <div
class="mx_EventTile_body translate" class="mx_EventTile_body translate"
dir="auto" dir="auto"
> >
@ -411,14 +410,14 @@ exports[`<TextualBody /> renders plain-text m.text correctly should pillify a pe
</a> </a>
</bdi> </bdi>
</span> </span>
</span> </div>
`; `;
exports[`<TextualBody /> renders plain-text m.text correctly simple message renders as expected 1`] = ` exports[`<TextualBody /> renders plain-text m.text correctly simple message renders as expected 1`] = `
<span <div
class="mx_EventTile_body translate" class="mx_EventTile_body translate"
dir="auto" dir="auto"
> >
this is a plaintext message this is a plaintext message
</span> </div>
`; `;

View file

@ -26,12 +26,12 @@ exports[`<PinnedEventTile /> should render pinned event 1`] = `
<div <div
class="mx_MTextBody mx_EventTile_content" class="mx_MTextBody mx_EventTile_content"
> >
<span <div
class="mx_EventTile_body translate" class="mx_EventTile_body translate"
dir="auto" dir="auto"
> >
First pinned message First pinned message
</span> </div>
</div> </div>
</div> </div>
<div <div

View file

@ -101,12 +101,12 @@ exports[`<LayoutSwitcher /> should render 1`] = `
<div <div
class="mx_MTextBody mx_EventTile_content" class="mx_MTextBody mx_EventTile_content"
> >
<span <div
class="mx_EventTile_body translate" class="mx_EventTile_body translate"
dir="auto" dir="auto"
> >
Hey you. You're the best! Hey you. You're the best!
</span> </div>
</div> </div>
<div <div
aria-label="Message Actions" aria-label="Message Actions"
@ -217,12 +217,12 @@ exports[`<LayoutSwitcher /> should render 1`] = `
<div <div
class="mx_MTextBody mx_EventTile_content" class="mx_MTextBody mx_EventTile_content"
> >
<span <div
class="mx_EventTile_body translate" class="mx_EventTile_body translate"
dir="auto" dir="auto"
> >
Hey you. You're the best! Hey you. You're the best!
</span> </div>
</div> </div>
<div <div
aria-label="Message Actions" aria-label="Message Actions"
@ -333,12 +333,12 @@ exports[`<LayoutSwitcher /> should render 1`] = `
<div <div
class="mx_MTextBody mx_EventTile_content" class="mx_MTextBody mx_EventTile_content"
> >
<span <div
class="mx_EventTile_body translate" class="mx_EventTile_body translate"
dir="auto" dir="auto"
> >
Hey you. You're the best! Hey you. You're the best!
</span> </div>
</div> </div>
<div <div
aria-label="Message Actions" aria-label="Message Actions"

View file

@ -244,12 +244,12 @@ exports[`AppearanceUserSettingsTab should render 1`] = `
<div <div
class="mx_MTextBody mx_EventTile_content" class="mx_MTextBody mx_EventTile_content"
> >
<span <div
class="mx_EventTile_body translate" class="mx_EventTile_body translate"
dir="auto" dir="auto"
> >
Hey you. You're the best! Hey you. You're the best!
</span> </div>
</div> </div>
<div <div
aria-label="Message Actions" aria-label="Message Actions"
@ -360,12 +360,12 @@ exports[`AppearanceUserSettingsTab should render 1`] = `
<div <div
class="mx_MTextBody mx_EventTile_content" class="mx_MTextBody mx_EventTile_content"
> >
<span <div
class="mx_EventTile_body translate" class="mx_EventTile_body translate"
dir="auto" dir="auto"
> >
Hey you. You're the best! Hey you. You're the best!
</span> </div>
</div> </div>
<div <div
aria-label="Message Actions" aria-label="Message Actions"
@ -476,12 +476,12 @@ exports[`AppearanceUserSettingsTab should render 1`] = `
<div <div
class="mx_MTextBody mx_EventTile_content" class="mx_MTextBody mx_EventTile_content"
> >
<span <div
class="mx_EventTile_body translate" class="mx_EventTile_body translate"
dir="auto" dir="auto"
> >
Hey you. You're the best! Hey you. You're the best!
</span> </div>
</div> </div>
<div <div
aria-label="Message Actions" aria-label="Message Actions"

File diff suppressed because one or more lines are too long