Refactor MKeyVerificationRequest to shared view MVVM (#33461)
* Refactor key verification request to shared view * Fix prettier * add tests to pass coverage
This commit is contained in:
BIN
Binary file not shown.
|
After Width: | Height: | Size: 23 KiB |
BIN
Binary file not shown.
|
After Width: | Height: | Size: 23 KiB |
BIN
Binary file not shown.
|
After Width: | Height: | Size: 24 KiB |
@@ -40,6 +40,7 @@ export * from "./room/timeline/event-tile/actions/ActionBarView";
|
||||
export * from "./room/timeline/event-tile/EventTileView/DisambiguatedProfile";
|
||||
export * from "./room/timeline/event-tile/EventTileView/EncryptionEventView";
|
||||
export * from "./room/timeline/event-tile/EventTileView/EventTileBubble";
|
||||
export * from "./room/timeline/event-tile/EventTileView/MKeyVerificationRequestView";
|
||||
export * from "./room/timeline/event-tile/EventTileView/PinnedMessageBadge";
|
||||
export * from "./room/timeline/event-tile/EventTileView/TextualEventView";
|
||||
export * from "./room/timeline/event-tile/body/AudioPlayerView";
|
||||
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
/*
|
||||
* 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 {
|
||||
svg {
|
||||
color: var(--cpd-color-text-primary);
|
||||
}
|
||||
}
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* 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, { type JSX } from "react";
|
||||
|
||||
import type { Meta, StoryObj } from "@storybook/react-vite";
|
||||
import { useMockedViewModel } from "../../../../../core/viewmodel";
|
||||
import { withViewDocs } from "../../../../../../.storybook/withViewDocs";
|
||||
import { MKeyVerificationRequestView, type MKeyVerificationRequestViewSnapshot } from "./MKeyVerificationRequestView";
|
||||
|
||||
type MKeyVerificationRequestViewProps = MKeyVerificationRequestViewSnapshot & {
|
||||
className?: string;
|
||||
};
|
||||
|
||||
const MKeyVerificationRequestViewWrapperImpl = ({
|
||||
className,
|
||||
...snapshot
|
||||
}: MKeyVerificationRequestViewProps): JSX.Element => {
|
||||
const vm = useMockedViewModel(snapshot, {});
|
||||
|
||||
return <MKeyVerificationRequestView vm={vm} className={className} />;
|
||||
};
|
||||
|
||||
const MKeyVerificationRequestViewWrapper = withViewDocs(
|
||||
MKeyVerificationRequestViewWrapperImpl,
|
||||
MKeyVerificationRequestView,
|
||||
);
|
||||
|
||||
const meta = {
|
||||
title: "Timeline/Timeline Event/MKeyVerificationRequestView",
|
||||
component: MKeyVerificationRequestViewWrapper,
|
||||
tags: ["autodocs"],
|
||||
args: {
|
||||
title: "Alice wants to verify",
|
||||
subtitle: "Alice (@alice:example.org)",
|
||||
className: "",
|
||||
},
|
||||
} satisfies Meta<typeof MKeyVerificationRequestViewWrapper>;
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof meta>;
|
||||
|
||||
export const Received: Story = {};
|
||||
|
||||
export const SentByMe: Story = {
|
||||
args: {
|
||||
title: "You sent a verification request",
|
||||
subtitle: "Bob (@bob:example.org)",
|
||||
},
|
||||
};
|
||||
|
||||
export const WithTimestamp: Story = {
|
||||
args: {
|
||||
timestamp: <span>14:56</span>,
|
||||
},
|
||||
};
|
||||
+65
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* 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 { MockViewModel } from "../../../../../core/viewmodel";
|
||||
import { MKeyVerificationRequestView } from "./MKeyVerificationRequestView";
|
||||
import * as stories from "./MKeyVerificationRequestView.stories";
|
||||
|
||||
const { Received, SentByMe, WithTimestamp } = composeStories(stories);
|
||||
|
||||
describe("MKeyVerificationRequestView", () => {
|
||||
it("renders the Received story", () => {
|
||||
const { container } = render(<Received />);
|
||||
|
||||
expect(container).toMatchSnapshot();
|
||||
expect(screen.getByText("Alice wants to verify")).toBeInTheDocument();
|
||||
expect(screen.getByText("Alice (@alice:example.org)")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("renders the SentByMe story", () => {
|
||||
const { container } = render(<SentByMe />);
|
||||
|
||||
expect(container).toMatchSnapshot();
|
||||
expect(screen.getByText("You sent a verification request")).toBeInTheDocument();
|
||||
expect(screen.getByText("Bob (@bob:example.org)")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("renders a timestamp", () => {
|
||||
const { container } = render(<WithTimestamp />);
|
||||
|
||||
expect(container).toMatchSnapshot();
|
||||
expect(screen.getByText("14:56")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("applies a custom className to the root element", () => {
|
||||
const vm = new MockViewModel({
|
||||
title: "Alice wants to verify",
|
||||
subtitle: "Alice (@alice:example.org)",
|
||||
});
|
||||
const { container } = render(<MKeyVerificationRequestView vm={vm} className="custom-verification" />);
|
||||
|
||||
expect(container.firstChild).toHaveClass("custom-verification");
|
||||
});
|
||||
|
||||
it("forwards the provided ref to the root element", () => {
|
||||
const ref = React.createRef<HTMLDivElement>() as React.RefObject<HTMLDivElement>;
|
||||
const vm = new MockViewModel({
|
||||
title: "Alice wants to verify",
|
||||
subtitle: "Alice (@alice:example.org)",
|
||||
});
|
||||
|
||||
render(<MKeyVerificationRequestView vm={vm} ref={ref} />);
|
||||
|
||||
expect(ref.current).toBeInstanceOf(HTMLDivElement);
|
||||
expect(ref.current).toHaveTextContent("Alice wants to verify");
|
||||
});
|
||||
});
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* 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 } from "react";
|
||||
import { LockSolidIcon } from "@vector-im/compound-design-tokens/assets/web/icons";
|
||||
|
||||
import { type ViewModel, useViewModel } from "../../../../../core/viewmodel";
|
||||
import { EventTileBubble } from "../EventTileBubble";
|
||||
import styles from "./MKeyVerificationRequestView.module.css";
|
||||
|
||||
export interface MKeyVerificationRequestViewSnapshot {
|
||||
/**
|
||||
* Main title text for the verification request.
|
||||
*/
|
||||
title: string;
|
||||
/**
|
||||
* Label for the other user involved in the request.
|
||||
*/
|
||||
subtitle: string;
|
||||
/**
|
||||
* Optional timestamp element rendered in the EventTileBubble footer slot.
|
||||
*/
|
||||
timestamp?: JSX.Element;
|
||||
}
|
||||
|
||||
export type MKeyVerificationRequestViewModel = ViewModel<MKeyVerificationRequestViewSnapshot>;
|
||||
|
||||
export interface MKeyVerificationRequestViewProps {
|
||||
/**
|
||||
* ViewModel providing the current verification request snapshot.
|
||||
*/
|
||||
vm: MKeyVerificationRequestViewModel;
|
||||
/**
|
||||
* Optional CSS classes passed through to EventTileBubble.
|
||||
*/
|
||||
className?: string;
|
||||
/**
|
||||
* Optional Ref forwarded to the root DOM element.
|
||||
*/
|
||||
ref?: React.RefObject<HTMLDivElement>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders a timeline bubble describing a key verification request message.
|
||||
*/
|
||||
export function MKeyVerificationRequestView({
|
||||
vm,
|
||||
className,
|
||||
ref,
|
||||
}: Readonly<MKeyVerificationRequestViewProps>): JSX.Element {
|
||||
const { title, subtitle, timestamp } = useViewModel(vm);
|
||||
|
||||
return (
|
||||
<EventTileBubble
|
||||
icon={<LockSolidIcon />}
|
||||
className={classNames(styles.content, className)}
|
||||
title={title}
|
||||
subtitle={subtitle}
|
||||
ref={ref}
|
||||
>
|
||||
{timestamp}
|
||||
</EventTileBubble>
|
||||
);
|
||||
}
|
||||
+94
@@ -0,0 +1,94 @@
|
||||
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
||||
|
||||
exports[`MKeyVerificationRequestView > renders a timestamp 1`] = `
|
||||
<div>
|
||||
<div
|
||||
class="EventTileBubble-module_container MKeyVerificationRequestView-module_content"
|
||||
>
|
||||
<svg
|
||||
fill="currentColor"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
width="1em"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M6 22q-.824 0-1.412-.587A1.93 1.93 0 0 1 4 20V10q0-.825.588-1.412A1.93 1.93 0 0 1 6 8h1V6q0-2.075 1.463-3.537Q9.926 1 12 1q2.075 0 3.537 1.463Q17 3.925 17 6v2h1q.824 0 1.413.588Q20 9.175 20 10v10q0 .824-.587 1.413A1.93 1.93 0 0 1 18 22zM9 8h6V6q0-1.25-.875-2.125A2.9 2.9 0 0 0 12 3q-1.25 0-2.125.875A2.9 2.9 0 0 0 9 6z"
|
||||
/>
|
||||
</svg>
|
||||
<div
|
||||
class="EventTileBubble-module_title"
|
||||
>
|
||||
Alice wants to verify
|
||||
</div>
|
||||
<div
|
||||
class="EventTileBubble-module_subtitle"
|
||||
>
|
||||
Alice (@alice:example.org)
|
||||
</div>
|
||||
<span>
|
||||
14:56
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`MKeyVerificationRequestView > renders the Received story 1`] = `
|
||||
<div>
|
||||
<div
|
||||
class="EventTileBubble-module_container MKeyVerificationRequestView-module_content"
|
||||
>
|
||||
<svg
|
||||
fill="currentColor"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
width="1em"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M6 22q-.824 0-1.412-.587A1.93 1.93 0 0 1 4 20V10q0-.825.588-1.412A1.93 1.93 0 0 1 6 8h1V6q0-2.075 1.463-3.537Q9.926 1 12 1q2.075 0 3.537 1.463Q17 3.925 17 6v2h1q.824 0 1.413.588Q20 9.175 20 10v10q0 .824-.587 1.413A1.93 1.93 0 0 1 18 22zM9 8h6V6q0-1.25-.875-2.125A2.9 2.9 0 0 0 12 3q-1.25 0-2.125.875A2.9 2.9 0 0 0 9 6z"
|
||||
/>
|
||||
</svg>
|
||||
<div
|
||||
class="EventTileBubble-module_title"
|
||||
>
|
||||
Alice wants to verify
|
||||
</div>
|
||||
<div
|
||||
class="EventTileBubble-module_subtitle"
|
||||
>
|
||||
Alice (@alice:example.org)
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`MKeyVerificationRequestView > renders the SentByMe story 1`] = `
|
||||
<div>
|
||||
<div
|
||||
class="EventTileBubble-module_container MKeyVerificationRequestView-module_content"
|
||||
>
|
||||
<svg
|
||||
fill="currentColor"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
width="1em"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M6 22q-.824 0-1.412-.587A1.93 1.93 0 0 1 4 20V10q0-.825.588-1.412A1.93 1.93 0 0 1 6 8h1V6q0-2.075 1.463-3.537Q9.926 1 12 1q2.075 0 3.537 1.463Q17 3.925 17 6v2h1q.824 0 1.413.588Q20 9.175 20 10v10q0 .824-.587 1.413A1.93 1.93 0 0 1 18 22zM9 8h6V6q0-1.25-.875-2.125A2.9 2.9 0 0 0 12 3q-1.25 0-2.125.875A2.9 2.9 0 0 0 9 6z"
|
||||
/>
|
||||
</svg>
|
||||
<div
|
||||
class="EventTileBubble-module_title"
|
||||
>
|
||||
You sent a verification request
|
||||
</div>
|
||||
<div
|
||||
class="EventTileBubble-module_subtitle"
|
||||
>
|
||||
Bob (@bob:example.org)
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
/*
|
||||
* 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 {
|
||||
MKeyVerificationRequestView,
|
||||
type MKeyVerificationRequestViewProps,
|
||||
type MKeyVerificationRequestViewSnapshot,
|
||||
type MKeyVerificationRequestViewModel,
|
||||
} from "./MKeyVerificationRequestView";
|
||||
Reference in New Issue
Block a user