Move unknown body to shared components (#33406)
* Move unknown body to shared components * added story snapshots
This commit is contained in:
@@ -238,7 +238,6 @@
|
|||||||
@import "./views/messages/_RoomAvatarEvent.pcss";
|
@import "./views/messages/_RoomAvatarEvent.pcss";
|
||||||
@import "./views/messages/_TextualEvent.pcss";
|
@import "./views/messages/_TextualEvent.pcss";
|
||||||
@import "./views/messages/_ThreadActionBar.pcss";
|
@import "./views/messages/_ThreadActionBar.pcss";
|
||||||
@import "./views/messages/_UnknownBody.pcss";
|
|
||||||
@import "./views/messages/_ViewSourceEvent.pcss";
|
@import "./views/messages/_ViewSourceEvent.pcss";
|
||||||
@import "./views/messages/_common_CryptoEvent.pcss";
|
@import "./views/messages/_common_CryptoEvent.pcss";
|
||||||
@import "./views/polls/pollHistory/_PollHistory.pcss";
|
@import "./views/polls/pollHistory/_PollHistory.pcss";
|
||||||
|
|||||||
@@ -1,11 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright 2024 New Vector Ltd.
|
|
||||||
Copyright 2015, 2016 OpenMarket Ltd
|
|
||||||
|
|
||||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
|
||||||
Please see LICENSE files in the repository root for full details.
|
|
||||||
*/
|
|
||||||
|
|
||||||
.mx_UnknownBody {
|
|
||||||
white-space: pre-wrap;
|
|
||||||
}
|
|
||||||
@@ -7,7 +7,7 @@ Please see LICENSE files in the repository root for full details.
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import mime from "mime";
|
import mime from "mime";
|
||||||
import React, { createRef } from "react";
|
import React, { createRef, type JSX } from "react";
|
||||||
import { logger } from "matrix-js-sdk/src/logger";
|
import { logger } from "matrix-js-sdk/src/logger";
|
||||||
import {
|
import {
|
||||||
EventType,
|
EventType,
|
||||||
@@ -18,10 +18,10 @@ import {
|
|||||||
M_POLL_START,
|
M_POLL_START,
|
||||||
type IContent,
|
type IContent,
|
||||||
} from "matrix-js-sdk/src/matrix";
|
} from "matrix-js-sdk/src/matrix";
|
||||||
|
import { UnknownBodyView } from "@element-hq/web-shared-components";
|
||||||
|
|
||||||
import SettingsStore from "../../../settings/SettingsStore";
|
import SettingsStore from "../../../settings/SettingsStore";
|
||||||
import { Mjolnir } from "../../../mjolnir/Mjolnir";
|
import { Mjolnir } from "../../../mjolnir/Mjolnir";
|
||||||
import UnknownBody from "./UnknownBody";
|
|
||||||
import { type IMediaBody } from "./IMediaBody";
|
import { type IMediaBody } from "./IMediaBody";
|
||||||
import { MediaEventHelper } from "../../../utils/MediaEventHelper";
|
import { MediaEventHelper } from "../../../utils/MediaEventHelper";
|
||||||
import { type IBodyProps } from "./IBodyProps";
|
import { type IBodyProps } from "./IBodyProps";
|
||||||
@@ -80,6 +80,10 @@ const baseEvTypes = new Map<string, React.ComponentType<IBodyProps>>([
|
|||||||
[M_BEACON_INFO.altName, MBeaconBody],
|
[M_BEACON_INFO.altName, MBeaconBody],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
function UnknownBody({ mxEvent, ref }: IBodyProps): JSX.Element {
|
||||||
|
return <UnknownBodyView text={mxEvent.getContent().body} ref={ref} className="mx_UnknownBody" />;
|
||||||
|
}
|
||||||
|
|
||||||
export default class MessageEvent extends React.Component<IProps> implements IMediaBody, IOperableEventTile {
|
export default class MessageEvent extends React.Component<IProps> implements IMediaBody, IOperableEventTile {
|
||||||
private body = createRef<React.Component | IOperableEventTile>();
|
private body = createRef<React.Component | IOperableEventTile>();
|
||||||
private mediaHelper?: MediaEventHelper;
|
private mediaHelper?: MediaEventHelper;
|
||||||
|
|||||||
@@ -1,21 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright 2024 New Vector Ltd.
|
|
||||||
Copyright 2020 The Matrix.org Foundation C.I.C.
|
|
||||||
Copyright 2015, 2016 OpenMarket Ltd
|
|
||||||
|
|
||||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
|
||||||
Please see LICENSE files in the repository root for full details.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import React, { type JSX } from "react";
|
|
||||||
|
|
||||||
import { type IBodyProps } from "./IBodyProps";
|
|
||||||
|
|
||||||
export default ({ mxEvent, ref }: IBodyProps): JSX.Element => {
|
|
||||||
const text = mxEvent.getContent().body;
|
|
||||||
return (
|
|
||||||
<div className="mx_UnknownBody" ref={ref}>
|
|
||||||
{text}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
@@ -19,11 +19,6 @@ import MessageEvent from "../../../../../src/components/views/messages/MessageEv
|
|||||||
import { RoomPermalinkCreator } from "../../../../../src/utils/permalinks/Permalinks";
|
import { RoomPermalinkCreator } from "../../../../../src/utils/permalinks/Permalinks";
|
||||||
import MatrixClientContext from "../../../../../src/contexts/MatrixClientContext";
|
import MatrixClientContext from "../../../../../src/contexts/MatrixClientContext";
|
||||||
|
|
||||||
jest.mock("../../../../../src/components/views/messages/UnknownBody", () => ({
|
|
||||||
__esModule: true,
|
|
||||||
default: () => <div data-testid="unknown-body" />,
|
|
||||||
}));
|
|
||||||
|
|
||||||
jest.mock("../../../../../src/components/views/messages/MImageBody", () => ({
|
jest.mock("../../../../../src/components/views/messages/MImageBody", () => ({
|
||||||
__esModule: true,
|
__esModule: true,
|
||||||
default: () => <div data-testid="image-body" />,
|
default: () => <div data-testid="image-body" />,
|
||||||
@@ -118,6 +113,25 @@ describe("MessageEvent", () => {
|
|||||||
expect(result.queryByTestId("textual-body")).toBeNull();
|
expect(result.queryByTestId("textual-body")).toBeNull();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("renders the shared unknown body for unsupported message types", () => {
|
||||||
|
event = mkEvent({
|
||||||
|
event: true,
|
||||||
|
type: EventType.RoomMessage,
|
||||||
|
user: "@alice:example.com",
|
||||||
|
room: room.roomId,
|
||||||
|
content: {
|
||||||
|
msgtype: "org.example.unsupported",
|
||||||
|
body: "Unsupported message body",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = renderMessageEvent();
|
||||||
|
|
||||||
|
expect(result.getByText("Unsupported message body")).toBeInTheDocument();
|
||||||
|
expect(result.container.querySelector(".mx_UnknownBody")).not.toBeNull();
|
||||||
|
expect(result.queryByTestId("textual-body")).toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
describe("when an image with a caption is sent", () => {
|
describe("when an image with a caption is sent", () => {
|
||||||
let result: RenderResult;
|
let result: RenderResult;
|
||||||
|
|
||||||
|
|||||||
BIN
Binary file not shown.
|
After Width: | Height: | Size: 20 KiB |
BIN
Binary file not shown.
|
After Width: | Height: | Size: 24 KiB |
@@ -24,6 +24,7 @@ export * from "./room/timeline/event-tile/body/MFileBodyView";
|
|||||||
export * from "./room/timeline/event-tile/body/MImageBodyView";
|
export * from "./room/timeline/event-tile/body/MImageBodyView";
|
||||||
export * from "./room/timeline/event-tile/body/MVideoBodyView";
|
export * from "./room/timeline/event-tile/body/MVideoBodyView";
|
||||||
export * from "./room/timeline/event-tile/body/TextualBodyView";
|
export * from "./room/timeline/event-tile/body/TextualBodyView";
|
||||||
|
export * from "./room/timeline/event-tile/body/UnknownBodyView";
|
||||||
export * from "./room/timeline/event-tile/EventTileView/TileErrorView";
|
export * from "./room/timeline/event-tile/EventTileView/TileErrorView";
|
||||||
export * from "./core/pill-input/Pill";
|
export * from "./core/pill-input/Pill";
|
||||||
export * from "./core/pill-input/PillInput";
|
export * from "./core/pill-input/PillInput";
|
||||||
|
|||||||
+10
@@ -0,0 +1,10 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2026 Element Creations Ltd.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||||
|
* Please see LICENSE files in the repository root for full details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
.content {
|
||||||
|
white-space: pre-wrap;
|
||||||
|
}
|
||||||
+33
@@ -0,0 +1,33 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2026 Element Creations Ltd.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||||
|
* Please see LICENSE files in the repository root for full details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import type { Meta, StoryObj } from "@storybook/react-vite";
|
||||||
|
import { withViewDocs } from "../../../../../../.storybook/withViewDocs";
|
||||||
|
import { UnknownBodyView } from "./UnknownBodyView";
|
||||||
|
|
||||||
|
const UnknownBodyViewWrapper = withViewDocs(UnknownBodyView, UnknownBodyView);
|
||||||
|
|
||||||
|
const meta = {
|
||||||
|
title: "Timeline/Timeline Body/UnknownBodyView",
|
||||||
|
component: UnknownBodyViewWrapper,
|
||||||
|
tags: ["autodocs"],
|
||||||
|
args: {
|
||||||
|
text: "Unsupported message body",
|
||||||
|
className: "",
|
||||||
|
},
|
||||||
|
} satisfies Meta<typeof UnknownBodyViewWrapper>;
|
||||||
|
|
||||||
|
export default meta;
|
||||||
|
type Story = StoryObj<typeof meta>;
|
||||||
|
|
||||||
|
export const Default: Story = {};
|
||||||
|
|
||||||
|
export const Multiline: Story = {
|
||||||
|
args: {
|
||||||
|
text: "Unsupported message body\nwith preserved line breaks",
|
||||||
|
},
|
||||||
|
};
|
||||||
+47
@@ -0,0 +1,47 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2026 Element Creations Ltd.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||||
|
* Please see LICENSE files in the repository root for full details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { composeStories } from "@storybook/react-vite";
|
||||||
|
import { render, screen } from "@test-utils";
|
||||||
|
import React from "react";
|
||||||
|
import { describe, expect, it } from "vitest";
|
||||||
|
|
||||||
|
import { UnknownBodyView } from "./UnknownBodyView";
|
||||||
|
import * as stories from "./UnknownBodyView.stories";
|
||||||
|
|
||||||
|
const { Default, Multiline } = composeStories(stories);
|
||||||
|
|
||||||
|
describe("UnknownBodyView", () => {
|
||||||
|
it("renders the default story", () => {
|
||||||
|
const { container } = render(<Default />);
|
||||||
|
|
||||||
|
expect(container).toMatchSnapshot();
|
||||||
|
expect(screen.getByText("Unsupported message body")).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("renders multiline content", () => {
|
||||||
|
const { container } = render(<Multiline />);
|
||||||
|
|
||||||
|
expect(container).toMatchSnapshot();
|
||||||
|
expect(screen.getByText(/with preserved line breaks/)).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("applies a custom className to the root element", () => {
|
||||||
|
const { container } = render(<UnknownBodyView text="Unsupported message body" className="custom-unknown" />);
|
||||||
|
|
||||||
|
expect(container.firstChild).toHaveClass("custom-unknown");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("forwards the provided ref to the root element", () => {
|
||||||
|
const ref = React.createRef<HTMLDivElement>();
|
||||||
|
|
||||||
|
render(<UnknownBodyView text="Unsupported message body" ref={ref} />);
|
||||||
|
|
||||||
|
expect(ref.current).toBeInstanceOf(HTMLDivElement);
|
||||||
|
expect(ref.current).toHaveTextContent("Unsupported message body");
|
||||||
|
});
|
||||||
|
});
|
||||||
+37
@@ -0,0 +1,37 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2026 Element Creations Ltd.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||||
|
* Please see LICENSE files in the repository root for full details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import classNames from "classnames";
|
||||||
|
import React, { type JSX, type ReactNode, type Ref } from "react";
|
||||||
|
|
||||||
|
import styles from "./UnknownBodyView.module.css";
|
||||||
|
|
||||||
|
export interface UnknownBodyViewProps {
|
||||||
|
/**
|
||||||
|
* Fallback message body content.
|
||||||
|
*/
|
||||||
|
text?: ReactNode;
|
||||||
|
/**
|
||||||
|
* Optional CSS class names applied to the root element.
|
||||||
|
*/
|
||||||
|
className?: string;
|
||||||
|
/**
|
||||||
|
* Optional ref forwarded to the root element.
|
||||||
|
*/
|
||||||
|
ref?: Ref<HTMLDivElement>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renders fallback body content for unsupported message types.
|
||||||
|
*/
|
||||||
|
export function UnknownBodyView({ text, className, ref }: Readonly<UnknownBodyViewProps>): JSX.Element {
|
||||||
|
return (
|
||||||
|
<div className={classNames(styles.content, className)} ref={ref}>
|
||||||
|
{text}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
+22
@@ -0,0 +1,22 @@
|
|||||||
|
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
||||||
|
|
||||||
|
exports[`UnknownBodyView > renders multiline content 1`] = `
|
||||||
|
<div>
|
||||||
|
<div
|
||||||
|
class="UnknownBodyView-module_content"
|
||||||
|
>
|
||||||
|
Unsupported message body
|
||||||
|
with preserved line breaks
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`UnknownBodyView > renders the default story 1`] = `
|
||||||
|
<div>
|
||||||
|
<div
|
||||||
|
class="UnknownBodyView-module_content"
|
||||||
|
>
|
||||||
|
Unsupported message body
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2026 Element Creations Ltd.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||||
|
* Please see LICENSE files in the repository root for full details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
export { UnknownBodyView, type UnknownBodyViewProps } from "./UnknownBodyView";
|
||||||
Reference in New Issue
Block a user