mirror of
https://github.com/vector-im/element-web.git
synced 2026-05-15 17:36:19 +02:00
Move hidden media placeholder to shared components (#33404)
* Move hidden media placeholder to shared components * Add Snapshots * Remove legacy hidden media mx class
This commit is contained in:
parent
1cc868a25a
commit
b5711264dd
@ -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";
|
||||
|
||||
@ -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;
|
||||
}
|
||||
@ -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<HTMLButtonElement>;
|
||||
}
|
||||
|
||||
export const HiddenMediaPlaceholder: React.FunctionComponent<PropsWithChildren<IProps>> = ({ onClick, children }) => {
|
||||
return (
|
||||
<button onClick={onClick} className="mx_HiddenMediaPlaceholder">
|
||||
<div>
|
||||
<VisibilityOnIcon />
|
||||
<span>{children}</span>
|
||||
</div>
|
||||
</button>
|
||||
);
|
||||
};
|
||||
@ -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";
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 18 KiB |
@ -18,6 +18,7 @@ export * from "./menus/UserMenu";
|
||||
export * from "./room/timeline/ReadMarker";
|
||||
export * from "./room/timeline/EventPresentation";
|
||||
export * from "./room/timeline/event-tile/body/EventContentBodyView";
|
||||
export * from "./room/timeline/event-tile/body/HiddenMediaPlaceholder";
|
||||
export * from "./room/timeline/event-tile/body/RedactedBodyView";
|
||||
export * from "./room/timeline/event-tile/body/MFileBodyView";
|
||||
export * from "./room/timeline/event-tile/body/MImageBodyView";
|
||||
|
||||
@ -0,0 +1,36 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
.button {
|
||||
border: none;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
padding: var(--cpd-space-0x);
|
||||
inset: var(--cpd-space-0x);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
background-color: var(--cpd-color-bg-subtle-secondary);
|
||||
}
|
||||
|
||||
.button:hover,
|
||||
.button:focus-visible {
|
||||
background-color: var(--cpd-color-bg-canvas-default);
|
||||
}
|
||||
|
||||
.content {
|
||||
color: var(--cpd-color-text-action-accent);
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: var(--cpd-space-1x);
|
||||
}
|
||||
|
||||
.icon {
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
@ -0,0 +1,38 @@
|
||||
/*
|
||||
* 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 React from "react";
|
||||
import { fn } from "storybook/test";
|
||||
|
||||
import type { Meta, StoryObj } from "@storybook/react-vite";
|
||||
import { withViewDocs } from "../../../../../../.storybook/withViewDocs";
|
||||
import { HiddenMediaPlaceholder } from "./HiddenMediaPlaceholder";
|
||||
|
||||
const HiddenMediaPlaceholderWrapper = withViewDocs(HiddenMediaPlaceholder, HiddenMediaPlaceholder);
|
||||
|
||||
const meta = {
|
||||
title: "Timeline/Timeline Body/HiddenMediaPlaceholder",
|
||||
component: HiddenMediaPlaceholderWrapper,
|
||||
tags: ["autodocs"],
|
||||
args: {
|
||||
children: "Show image",
|
||||
onClick: fn(),
|
||||
className: "",
|
||||
},
|
||||
decorators: [
|
||||
(Story) => (
|
||||
<div style={{ width: 320, height: 180 }}>
|
||||
<Story />
|
||||
</div>
|
||||
),
|
||||
],
|
||||
} satisfies Meta<typeof HiddenMediaPlaceholderWrapper>;
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof meta>;
|
||||
|
||||
export const Default: Story = {};
|
||||
@ -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(<Default />);
|
||||
|
||||
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(<HiddenMediaPlaceholder onClick={onClick}>Show image</HiddenMediaPlaceholder>);
|
||||
|
||||
await user.click(screen.getByRole("button", { name: "Show image" }));
|
||||
|
||||
expect(onClick).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it("applies a custom className to the root button", () => {
|
||||
render(
|
||||
<HiddenMediaPlaceholder onClick={vi.fn()} className="custom-hidden-media">
|
||||
Show image
|
||||
</HiddenMediaPlaceholder>,
|
||||
);
|
||||
|
||||
expect(screen.getByRole("button", { name: "Show image" })).toHaveClass("custom-hidden-media");
|
||||
});
|
||||
});
|
||||
@ -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<HTMLButtonElement>;
|
||||
}>;
|
||||
|
||||
/**
|
||||
* Renders a full-frame button used to reveal hidden media previews.
|
||||
*/
|
||||
export function HiddenMediaPlaceholder({
|
||||
className,
|
||||
onClick,
|
||||
children,
|
||||
}: Readonly<HiddenMediaPlaceholderProps>): JSX.Element {
|
||||
return (
|
||||
<button type="button" onClick={onClick} className={classNames(styles.button, className)}>
|
||||
<span className={styles.content}>
|
||||
<VisibilityOnIcon className={styles.icon} aria-hidden="true" />
|
||||
<span>{children}</span>
|
||||
</span>
|
||||
</button>
|
||||
);
|
||||
}
|
||||
@ -0,0 +1,35 @@
|
||||
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
||||
|
||||
exports[`HiddenMediaPlaceholder > renders the default story 1`] = `
|
||||
<div>
|
||||
<div
|
||||
style="width: 320px; height: 180px;"
|
||||
>
|
||||
<button
|
||||
class="HiddenMediaPlaceholder-module_button"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
class="HiddenMediaPlaceholder-module_content"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class="HiddenMediaPlaceholder-module_icon"
|
||||
fill="currentColor"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
width="1em"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M12 16q1.875 0 3.188-1.312Q16.5 13.375 16.5 11.5t-1.312-3.187T12 7 8.813 8.313 7.5 11.5t1.313 3.188T12 16m0-1.8q-1.125 0-1.912-.787A2.6 2.6 0 0 1 9.3 11.5q0-1.125.787-1.912A2.6 2.6 0 0 1 12 8.8q1.125 0 1.912.787.788.788.788 1.913t-.787 1.912A2.6 2.6 0 0 1 12 14.2m0 4.8q-3.475 0-6.35-1.837Q2.775 15.324 1.3 12.2a.8.8 0 0 1-.1-.312 3 3 0 0 1 0-.775.8.8 0 0 1 .1-.313q1.475-3.125 4.35-4.962Q8.525 4 12 4t6.35 1.838T22.7 10.8a.8.8 0 0 1 .1.313 3 3 0 0 1 0 .774.8.8 0 0 1-.1.313q-1.475 3.125-4.35 4.963Q15.475 19 12 19m0-2a9.54 9.54 0 0 0 5.188-1.488A9.77 9.77 0 0 0 20.8 11.5a9.77 9.77 0 0 0-3.613-4.012A9.54 9.54 0 0 0 12 6a9.55 9.55 0 0 0-5.187 1.487A9.77 9.77 0 0 0 3.2 11.5a9.77 9.77 0 0 0 3.613 4.012A9.54 9.54 0 0 0 12 17"
|
||||
/>
|
||||
</svg>
|
||||
<span>
|
||||
Show image
|
||||
</span>
|
||||
</span>
|
||||
</button>
|
||||
</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 { HiddenMediaPlaceholder, type HiddenMediaPlaceholderProps } from "./HiddenMediaPlaceholder";
|
||||
Loading…
x
Reference in New Issue
Block a user