From b5711264dd9fab81fda040a2d72ad36d4a134976 Mon Sep 17 00:00:00 2001 From: Zack Date: Wed, 6 May 2026 13:23:14 +0200 Subject: [PATCH] Move hidden media placeholder to shared components (#33404) * Move hidden media placeholder to shared components * Add Snapshots * Remove legacy hidden media mx class --- apps/web/res/css/_components.pcss | 1 - .../messages/_HiddenMediaPlaceholder.pcss | 29 ----------- .../views/messages/HiddenMediaPlaceholder.tsx | 24 --------- .../components/views/messages/MImageBody.tsx | 2 +- .../default-auto.png | Bin 0 -> 18407 bytes packages/shared-components/src/index.ts | 1 + .../HiddenMediaPlaceholder.module.css | 36 ++++++++++++++ .../HiddenMediaPlaceholder.stories.tsx | 38 ++++++++++++++ .../HiddenMediaPlaceholder.test.tsx | 47 ++++++++++++++++++ .../HiddenMediaPlaceholder.tsx | 41 +++++++++++++++ .../HiddenMediaPlaceholder.test.tsx.snap | 35 +++++++++++++ .../body/HiddenMediaPlaceholder/index.tsx | 8 +++ 12 files changed, 207 insertions(+), 55 deletions(-) delete mode 100644 apps/web/res/css/views/messages/_HiddenMediaPlaceholder.pcss delete mode 100644 apps/web/src/components/views/messages/HiddenMediaPlaceholder.tsx create mode 100644 packages/shared-components/__vis__/linux/__baselines__/room/timeline/event-tile/body/HiddenMediaPlaceholder/HiddenMediaPlaceholder.stories.tsx/default-auto.png create mode 100644 packages/shared-components/src/room/timeline/event-tile/body/HiddenMediaPlaceholder/HiddenMediaPlaceholder.module.css create mode 100644 packages/shared-components/src/room/timeline/event-tile/body/HiddenMediaPlaceholder/HiddenMediaPlaceholder.stories.tsx create mode 100644 packages/shared-components/src/room/timeline/event-tile/body/HiddenMediaPlaceholder/HiddenMediaPlaceholder.test.tsx create mode 100644 packages/shared-components/src/room/timeline/event-tile/body/HiddenMediaPlaceholder/HiddenMediaPlaceholder.tsx create mode 100644 packages/shared-components/src/room/timeline/event-tile/body/HiddenMediaPlaceholder/__snapshots__/HiddenMediaPlaceholder.test.tsx.snap create mode 100644 packages/shared-components/src/room/timeline/event-tile/body/HiddenMediaPlaceholder/index.tsx diff --git a/apps/web/res/css/_components.pcss b/apps/web/res/css/_components.pcss index 1813d36d34..4486e69a5d 100644 --- a/apps/web/res/css/_components.pcss +++ b/apps/web/res/css/_components.pcss @@ -223,7 +223,6 @@ @import "./views/messages/_CreateEvent.pcss"; @import "./views/messages/_DisambiguatedProfile.pcss"; @import "./views/messages/_HiddenBody.pcss"; -@import "./views/messages/_HiddenMediaPlaceholder.pcss"; @import "./views/messages/_LegacyCallEvent.pcss"; @import "./views/messages/_MFileBody.pcss"; @import "./views/messages/_MImageBody.pcss"; diff --git a/apps/web/res/css/views/messages/_HiddenMediaPlaceholder.pcss b/apps/web/res/css/views/messages/_HiddenMediaPlaceholder.pcss deleted file mode 100644 index c7efe6ec7e..0000000000 --- a/apps/web/res/css/views/messages/_HiddenMediaPlaceholder.pcss +++ /dev/null @@ -1,29 +0,0 @@ -.mx_HiddenMediaPlaceholder { - border: none; - width: 100%; - height: 100%; - inset: 0; - - /* To center the text in the middle of the frame */ - display: flex; - align-items: center; - justify-content: center; - text-align: center; - - cursor: pointer; - background-color: $header-panel-bg-color; - - > div { - color: $accent; - /* Icon alignment */ - display: flex; - > svg { - margin-top: auto; - margin-bottom: auto; - } - } -} - -.mx_EventTile:hover .mx_HiddenMediaPlaceholder { - background-color: $background; -} diff --git a/apps/web/src/components/views/messages/HiddenMediaPlaceholder.tsx b/apps/web/src/components/views/messages/HiddenMediaPlaceholder.tsx deleted file mode 100644 index 16367ee05a..0000000000 --- a/apps/web/src/components/views/messages/HiddenMediaPlaceholder.tsx +++ /dev/null @@ -1,24 +0,0 @@ -/* -Copyright 2025 New Vector 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 PropsWithChildren, type MouseEventHandler } from "react"; -import { VisibilityOnIcon } from "@vector-im/compound-design-tokens/assets/web/icons"; - -interface IProps { - onClick: MouseEventHandler; -} - -export const HiddenMediaPlaceholder: React.FunctionComponent> = ({ onClick, children }) => { - return ( - - ); -}; diff --git a/apps/web/src/components/views/messages/MImageBody.tsx b/apps/web/src/components/views/messages/MImageBody.tsx index 2d1b1402e5..351a40e000 100644 --- a/apps/web/src/components/views/messages/MImageBody.tsx +++ b/apps/web/src/components/views/messages/MImageBody.tsx @@ -16,6 +16,7 @@ import { ClientEvent } from "matrix-js-sdk/src/matrix"; import { type ImageContent } from "matrix-js-sdk/src/types"; import { Tooltip } from "@vector-im/compound-web"; import { ImageErrorIcon } from "@vector-im/compound-design-tokens/assets/web/icons"; +import { HiddenMediaPlaceholder } from "@element-hq/web-shared-components"; import Modal from "../../../Modal"; import { _t } from "../../../languageHandler"; @@ -33,7 +34,6 @@ import { presentableTextForFile } from "../../../utils/FileUtils"; import { createReconnectedListener } from "../../../utils/connection"; import MediaProcessingError from "./shared/MediaProcessingError"; import { DecryptError, DownloadError } from "../../../utils/DecryptFile"; -import { HiddenMediaPlaceholder } from "./HiddenMediaPlaceholder"; import { useMediaVisible } from "../../../hooks/useMediaVisible"; import { isMimeTypeAllowed } from "../../../utils/blobs.ts"; import { FileBodyFactory, renderMBody } from "./MBodyFactory"; diff --git a/packages/shared-components/__vis__/linux/__baselines__/room/timeline/event-tile/body/HiddenMediaPlaceholder/HiddenMediaPlaceholder.stories.tsx/default-auto.png b/packages/shared-components/__vis__/linux/__baselines__/room/timeline/event-tile/body/HiddenMediaPlaceholder/HiddenMediaPlaceholder.stories.tsx/default-auto.png new file mode 100644 index 0000000000000000000000000000000000000000..2a3e46c6ab8ac2b69786937ec1683bc70ddf6e11 GIT binary patch literal 18407 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 ( +
+ +
+ ), + ], +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +export const Default: Story = {}; diff --git a/packages/shared-components/src/room/timeline/event-tile/body/HiddenMediaPlaceholder/HiddenMediaPlaceholder.test.tsx b/packages/shared-components/src/room/timeline/event-tile/body/HiddenMediaPlaceholder/HiddenMediaPlaceholder.test.tsx new file mode 100644 index 0000000000..6122407ef8 --- /dev/null +++ b/packages/shared-components/src/room/timeline/event-tile/body/HiddenMediaPlaceholder/HiddenMediaPlaceholder.test.tsx @@ -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 userEvent from "@testing-library/user-event"; +import { describe, expect, it, vi } from "vitest"; + +import { HiddenMediaPlaceholder } from "./HiddenMediaPlaceholder"; +import * as stories from "./HiddenMediaPlaceholder.stories"; + +const { Default } = composeStories(stories); + +describe("HiddenMediaPlaceholder", () => { + it("renders the default story", () => { + const { container } = render(); + + expect(container).toMatchSnapshot(); + expect(screen.getByRole("button", { name: "Show image" })).toBeInTheDocument(); + }); + + it("invokes the click handler", async () => { + const user = userEvent.setup(); + const onClick = vi.fn(); + + render(Show image); + + await user.click(screen.getByRole("button", { name: "Show image" })); + + expect(onClick).toHaveBeenCalledTimes(1); + }); + + it("applies a custom className to the root button", () => { + render( + + Show image + , + ); + + expect(screen.getByRole("button", { name: "Show image" })).toHaveClass("custom-hidden-media"); + }); +}); diff --git a/packages/shared-components/src/room/timeline/event-tile/body/HiddenMediaPlaceholder/HiddenMediaPlaceholder.tsx b/packages/shared-components/src/room/timeline/event-tile/body/HiddenMediaPlaceholder/HiddenMediaPlaceholder.tsx new file mode 100644 index 0000000000..4aa3af9d54 --- /dev/null +++ b/packages/shared-components/src/room/timeline/event-tile/body/HiddenMediaPlaceholder/HiddenMediaPlaceholder.tsx @@ -0,0 +1,41 @@ +/* + * 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 MouseEventHandler, type PropsWithChildren } from "react"; +import { VisibilityOnIcon } from "@vector-im/compound-design-tokens/assets/web/icons"; + +import styles from "./HiddenMediaPlaceholder.module.css"; + +export type HiddenMediaPlaceholderProps = PropsWithChildren<{ + /** + * CSS class names applied to the root button. + */ + className?: string; + /** + * Invoked when the user chooses to reveal the hidden media. + */ + onClick: MouseEventHandler; +}>; + +/** + * Renders a full-frame button used to reveal hidden media previews. + */ +export function HiddenMediaPlaceholder({ + className, + onClick, + children, +}: Readonly): JSX.Element { + return ( + + ); +} diff --git a/packages/shared-components/src/room/timeline/event-tile/body/HiddenMediaPlaceholder/__snapshots__/HiddenMediaPlaceholder.test.tsx.snap b/packages/shared-components/src/room/timeline/event-tile/body/HiddenMediaPlaceholder/__snapshots__/HiddenMediaPlaceholder.test.tsx.snap new file mode 100644 index 0000000000..11ecde221d --- /dev/null +++ b/packages/shared-components/src/room/timeline/event-tile/body/HiddenMediaPlaceholder/__snapshots__/HiddenMediaPlaceholder.test.tsx.snap @@ -0,0 +1,35 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`HiddenMediaPlaceholder > renders the default story 1`] = ` +
+
+ +
+
+`; diff --git a/packages/shared-components/src/room/timeline/event-tile/body/HiddenMediaPlaceholder/index.tsx b/packages/shared-components/src/room/timeline/event-tile/body/HiddenMediaPlaceholder/index.tsx new file mode 100644 index 0000000000..a332447ea6 --- /dev/null +++ b/packages/shared-components/src/room/timeline/event-tile/body/HiddenMediaPlaceholder/index.tsx @@ -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 { HiddenMediaPlaceholder, type HiddenMediaPlaceholderProps } from "./HiddenMediaPlaceholder";