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
113
playwright/e2e/messages/messages.spec.ts
Normal 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")],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
After Width: | Height: | Size: 4 KiB |
After Width: | Height: | Size: 4 KiB |
After Width: | Height: | Size: 4 KiB |
After Width: | Height: | Size: 4 KiB |
After Width: | Height: | Size: 4.6 KiB |
After Width: | Height: | Size: 4.7 KiB |
After Width: | Height: | Size: 4.8 KiB |
After Width: | Height: | Size: 4.9 KiB |
After Width: | Height: | Size: 3.7 KiB |
After Width: | Height: | Size: 3.9 KiB |
After Width: | Height: | Size: 3.7 KiB |
After Width: | Height: | Size: 3.5 KiB |
|
@ -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 {
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -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,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -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">
|
||||||
*
|
*
|
||||||
<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()}
|
||||||
|
|
|
@ -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();
|
||||||
});
|
});
|
||||||
|
|
|
@ -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>
|
||||||
`;
|
`;
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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"
|
||||||
|
|