Tweak new user menu design (#33444)

* Tweak new user menu design

* Update screenshots

* More screenshots

* Add story with no avatar

* add story to test

* Expand the menu so it actually shows what it's supposed to show

* Use the open parameter

which is the param to open the menu

* snapshot

* Add test for normal open menu version

* Move menu to the right of the avatar

and make it appear on top of the display name

* Fix user menu layout

* screenshots

* snapshot

* screenshots

* more screenshots

* Shift toasts to the left slightly

So they completely cover the display name of the user menu when
it's expanded. Also down slightly so they're level with the user menu.

* Tweak toast border radius & position

to match & sit on top of the user menu

* Close toasts in cider tests

* Only close the toast if it's actually there

* Fix some toasts to be dismissed & update screenshots

more to come though

* Make closeKeyStorageToast actually optional

* Fix some more toasts & races

* Screenshots again again (again)

* More screenshots

* Convert to rejectToast

* Fix toast rejectors to not wait and update screenshots

* Apparently 1ms is not long enough
This commit is contained in:
David Baker
2026-05-19 14:10:14 +01:00
committed by GitHub
parent 00ccc97c79
commit 0140563b64
46 changed files with 1011 additions and 255 deletions
@@ -6,6 +6,8 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Com
Please see LICENSE files in the repository root for full details.
*/
import { rejectToastIfExists } from "@element-hq/element-web-playwright-common";
import { test, expect } from "../../element-web-test";
import { SettingLevel } from "../../../src/settings/SettingLevel";
import { getSampleFilePath } from "../../sample-files";
@@ -28,7 +30,9 @@ test.describe("Composer", () => {
},
});
test.beforeEach(async ({ room }) => {}); // trigger room fixture
test.beforeEach(async ({ app, room /* trigger room fixture */ }) => {
await rejectToastIfExists(app.page, "Notifications");
});
test.describe("CIDER", () => {
test("sends a message when you click send or press Enter", async ({ page }) => {
@@ -6,6 +6,8 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Com
Please see LICENSE files in the repository root for full details.
*/
import { rejectToastIfExists } from "@element-hq/element-web-playwright-common";
import type { Preset, RoomMemberEvent, RoomStateEvent } from "matrix-js-sdk/src/matrix";
import { expect, test } from "../../element-web-test";
import {
@@ -118,6 +120,9 @@ test.describe("Cryptography", function () {
user: alice,
bot: bob,
}) => {
await rejectToastIfExists(page, "Verify this device");
await rejectToastIfExists(page, "Notifications");
// Bob creates an encrypted room and sends a message to it. He then invites Alice
const roomId = await bob.evaluate(
async (client, { alice }) => {
@@ -224,6 +229,9 @@ test.describe("Cryptography", function () {
user: alice,
bot: bob,
}) => {
await rejectToastIfExists(page, "Verify this device");
await rejectToastIfExists(page, "Notifications");
// Bob:
// - creates an encrypted room,
// - invites Alice,
@@ -6,6 +6,8 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Com
Please see LICENSE files in the repository root for full details.
*/
import { rejectToastIfExists } from "@element-hq/element-web-playwright-common";
import { test, expect } from "../../element-web-test";
import { isDendrite } from "../../plugins/homeserver/dendrite";
@@ -46,6 +48,8 @@ test.describe("Mark as Unread", () => {
// navigate away from the room again
await page.goto("/#/room/" + dummyRoomId);
await rejectToastIfExists(page, "Verify this device");
const roomTile = page.getByLabel(TEST_ROOM_NAME);
await roomTile.click({ button: "right" });
await page.getByRole("menuitem", { name: "Mark as unread" }).click();
@@ -69,6 +69,7 @@ test.describe("Appearance user settings tab", () => {
{ tag: "@screenshot" },
async ({ page, app, user, util }) => {
await rejectToast(page, "Verify this device");
await rejectToast(page, "Notifications");
const roomId = await util.createAndDisplayRoom();
@@ -28,6 +28,7 @@ test.describe("Threads Activity Centre", { tag: "@no-firefox" }, () => {
{ tag: "@screenshot" },
async ({ util, page }) => {
await rejectToast(page, "Verify this device");
await rejectToast(page, "Notifications");
// Open the space panel
await util.expandSpacePanel();
@@ -41,6 +42,7 @@ test.describe("Threads Activity Centre", { tag: "@no-firefox" }, () => {
{ tag: "@screenshot" },
async ({ room1, util, app, page }) => {
await rejectToast(page, "Verify this device");
await rejectToast(page, "Notifications");
// No indicator should be shown
await util.assertNoTacIndicator();
@@ -61,6 +63,7 @@ test.describe("Threads Activity Centre", { tag: "@no-firefox" }, () => {
page,
}) => {
await rejectToast(page, "Verify this device");
await rejectToast(page, "Notifications");
await util.goTo(room1);
await util.receiveMessages(room1, ["Msg1", msg.threadedOff("Msg1", "Resp1")]);
@@ -78,6 +81,7 @@ test.describe("Threads Activity Centre", { tag: "@no-firefox" }, () => {
page,
}) => {
await rejectToast(page, "Verify this device");
await rejectToast(page, "Notifications");
await util.goTo(room1);
await util.receiveMessages(room1, [
@@ -101,6 +105,7 @@ test.describe("Threads Activity Centre", { tag: "@no-firefox" }, () => {
{ tag: "@screenshot" },
async ({ room1, room2, util, msg, user, app, page }) => {
await rejectToast(page, "Verify this device");
await rejectToast(page, "Notifications");
await util.goTo(room2);
await util.populateThreads(room1, room2, msg, user);
@@ -124,6 +129,7 @@ test.describe("Threads Activity Centre", { tag: "@no-firefox" }, () => {
{ tag: "@screenshot" },
async ({ room1, room2, util, msg, user, app, page }) => {
await rejectToast(page, "Verify this device");
await rejectToast(page, "Notifications");
await util.goTo(room2);
await util.populateThreads(room1, room2, msg, user);
@@ -150,6 +156,7 @@ test.describe("Threads Activity Centre", { tag: "@no-firefox" }, () => {
test("should order by recency after notification level", async ({ room1, room2, util, msg, user, app, page }) => {
await rejectToast(page, "Verify this device");
await rejectToast(page, "Notifications");
await util.goTo(room2);
await util.populateThreads(room1, room2, msg, user, false);
@@ -163,6 +170,7 @@ test.describe("Threads Activity Centre", { tag: "@no-firefox" }, () => {
test("should block the Spotlight to open when the TAC is opened", async ({ util, page, app }) => {
await rejectToast(page, "Verify this device");
await rejectToast(page, "Notifications");
const toggleSpotlight = () => page.keyboard.press(`${CommandOrControl}+k`);
@@ -180,6 +188,7 @@ test.describe("Threads Activity Centre", { tag: "@no-firefox" }, () => {
test("should have the correct hover state", { tag: "@screenshot" }, async ({ util, page, app }) => {
await rejectToast(page, "Verify this device");
await rejectToast(page, "Notifications");
await util.hoverTacButton();
await expect(util.getSpacePanel()).toMatchScreenshot("tac-hovered.png");
@@ -192,6 +201,7 @@ test.describe("Threads Activity Centre", { tag: "@no-firefox" }, () => {
test("should mark all threads as read", { tag: "@screenshot" }, async ({ room1, room2, util, msg, page, app }) => {
await rejectToast(page, "Verify this device");
await rejectToast(page, "Notifications");
await util.receiveMessages(room1, ["Msg1", msg.threadedOff("Msg1", "Resp1")]);
@@ -214,6 +224,7 @@ test.describe("Threads Activity Centre", { tag: "@no-firefox" }, () => {
page,
}) => {
await rejectToast(page, "Verify this device");
await rejectToast(page, "Notifications");
await util.receiveMessages(room1, ["Msg1", msg.threadedOff("Msg1", "Resp1")]);
+13 -2
View File
@@ -9,7 +9,7 @@ Please see LICENSE files in the repository root for full details.
import { type Locator, type Page, expect } from "@playwright/test";
import { readFile } from "node:fs/promises";
import { basename } from "node:path";
import { rejectToast } from "@element-hq/element-web-playwright-common";
import { rejectToastIfExists } from "@element-hq/element-web-playwright-common";
import { Settings } from "./settings";
import { Client } from "./client";
@@ -96,6 +96,17 @@ export class ElementAppPage {
* @param name The exact room name to find and click on/open.
*/
public async viewRoomByName(name: string): Promise<void> {
// Make sure the room list is actually present before we try closing toasts,
// otherwise we may race with page loading
await this.page.getByTestId("room-list").waitFor();
await rejectToastIfExists(this.page, "Verify this device", { timeout: 50 });
const keyStorageToastRejected = await rejectToastIfExists(this.page, "Turn on key storage", { timeout: 50 });
if (keyStorageToastRejected) {
await this.page.getByRole("button", { name: "Yes, dismiss" }).click();
}
await rejectToastIfExists(this.page, "Notifications", { timeout: 50 });
// We get the room list by test-id which is a listbox and matching title=name
return this.page.getByTestId("room-list").locator(`[title="${name}"]`).first().click();
}
@@ -363,7 +374,7 @@ export class ElementAppPage {
* Dismiss the "Turn on key storage" toast.
*/
public async closeKeyStorageToast() {
await rejectToast(this.page, "Turn on key storage");
await this.closeToast("Turn on key storage", "Dismiss");
await this.page.getByRole("button", { name: "Yes, dismiss" }).click();
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.8 KiB

After

Width:  |  Height:  |  Size: 9.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 56 KiB

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 75 KiB

After

Width:  |  Height:  |  Size: 75 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 71 KiB

After

Width:  |  Height:  |  Size: 71 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 92 KiB

After

Width:  |  Height:  |  Size: 92 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 56 KiB

After

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 74 KiB

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 75 KiB

After

Width:  |  Height:  |  Size: 69 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 56 KiB

After

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.2 KiB

After

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.7 KiB

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.7 KiB

After

Width:  |  Height:  |  Size: 9.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 9.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

@@ -9,10 +9,9 @@ Please see LICENSE files in the repository root for full details.
.mx_ToastContainer {
position: absolute;
top: 0;
left: 70px;
top: 12px;
left: 62px;
z-index: 101;
padding: 4px;
display: grid;
grid-template-rows: 1fr 28px 8px;
@@ -33,7 +32,7 @@ Please see LICENSE files in the repository root for full details.
color: $primary-content;
box-shadow: 0px 4px 24px rgb(0, 0, 0, 0.1);
border: var(--cpd-border-width-1) solid var(--cpd-color-border-interactive-secondary);
border-radius: calc(var(--cpd-space-6x) - var(--cpd-border-width-1));
border-radius: var(--cpd-space-3x);
overflow: hidden;
display: grid;
grid-template-columns: 20px 1fr auto;
@@ -6,26 +6,30 @@ exports[`<SpacePanel /> should show all activated MetaSpaces in the correct orde
aria-label="Spaces"
class="mx_SpacePanel collapsed newUi"
>
<button
aria-expanded="false"
aria-haspopup="menu"
aria-label="User menu"
class="_triggerButton_1ejl3_50 mx_UserMenu"
data-state="closed"
id="radix-_r_0_"
type="button"
<div
class="_wrapper_1yvpq_8"
>
<span
aria-label="@test:test"
class="_avatar_va14e_8 _avatar-imageless_va14e_55"
data-color="5"
data-type="round"
role="img"
style="--cpd-avatar-size: 36px;"
<button
aria-expanded="false"
aria-haspopup="menu"
aria-label="User menu"
class="_triggerButton_1yvpq_57 mx_UserMenu"
data-state="closed"
id="radix-_r_0_"
type="button"
>
t
</span>
</button>
<span
aria-label="@test:test"
class="_avatar_va14e_8 _avatar-imageless_va14e_55"
data-color="5"
data-type="round"
role="img"
style="--cpd-avatar-size: 36px;"
>
t
</span>
</button>
</div>
<div
aria-label="Expand"
class="mx_AccessibleButton mx_SpacePanel_toggleCollapse"
+19 -6
View File
@@ -63,7 +63,7 @@ export async function getToastIfExists(page: Page, title: string, timeout = 2000
* @param title - Expected title of the toast.
*/
export async function acceptToast(page: Page, title: string): Promise<void> {
return await clickToastButton(page, title, "primary");
await clickToastButton(page, title, "primary");
}
/**
@@ -75,8 +75,10 @@ export async function acceptToast(page: Page, title: string): Promise<void> {
* @public
* @param page - Playwright page we are working with.
* @param title - Expected title of the toast.
*
* @returns true if the toast was found and the button was clicked, or false if the toast was not found (always returns true if `required` is true).
*/
export async function acceptToastIfExists(page: Page, title: string): Promise<void> {
export async function acceptToastIfExists(page: Page, title: string): Promise<boolean> {
return await clickToastButton(page, title, "primary", 2000, false);
}
@@ -90,7 +92,7 @@ export async function acceptToastIfExists(page: Page, title: string): Promise<vo
* @param title - Expected title of the toast.
*/
export async function rejectToast(page: Page, title: string): Promise<void> {
return await clickToastButton(page, title, "secondary");
await clickToastButton(page, title, "secondary");
}
/**
@@ -102,9 +104,17 @@ export async function rejectToast(page: Page, title: string): Promise<void> {
* @public
* @param page - Playwright page we are working with.
* @param title - Expected title of the toast.
* @param options.timeout - Time in ms before we give up and decide the toast does not exist.
*
* @returns true if the toast was found and the button was clicked, or false if the toast was not found (always returns true if `required` is true).
*/
export async function rejectToastIfExists(page: Page, title: string): Promise<void> {
return await clickToastButton(page, title, "secondary", 2000, false);
export async function rejectToastIfExists(
page: Page,
title: string,
options: { timeout?: number } = {},
): Promise<boolean> {
const { timeout = 2000 } = options;
return await clickToastButton(page, title, "secondary", timeout, false);
}
/**
@@ -127,6 +137,7 @@ export async function rejectToastIfExists(page: Page, title: string): Promise<vo
* @param required - If true, fail the test (throw an exception) if the
* toast is not visible. Otherwise, just return after
* `timeout` if the toast is not visible.
* @returns true if the toast was found and the button was clicked, or false if the toast was not found (always returns true if `required` is true).
*/
async function clickToastButton(
page: Page,
@@ -134,7 +145,7 @@ async function clickToastButton(
button: "primary" | "secondary",
timeout?: number,
required = true,
): Promise<void> {
): Promise<boolean> {
let toast: Locator | null;
if (required) {
toast = await getToast(page, title, timeout);
@@ -144,5 +155,7 @@ async function clickToastButton(
if (toast) {
await toast.locator(`.mx_Toast_buttons button[data-kind="${button}"]`).click();
return true;
}
return false;
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.1 KiB

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.2 KiB

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.4 KiB

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 50 KiB

After

Width:  |  Height:  |  Size: 44 KiB

@@ -5,6 +5,13 @@
* Please see LICENSE files in the repository root for full details.
*/
.wrapper {
display: flex;
align-items: center;
margin-top: var(--cpd-space-3x);
margin-bottom: var(--cpd-space-4x);
}
.container {
/* As per design, seperator should span whole width. */
div[role="separator"] {
@@ -50,6 +57,7 @@
.triggerButton {
border: none;
background: none;
padding: 0;
display: flex;
color: var(--cpd-color-body-primary);
cursor: pointer;
@@ -63,7 +71,7 @@
overflow: hidden;
text-wrap: nowrap;
}
margin-top: var(--cpd-space-3x);
margin-bottom: var(--cpd-space-4x);
margin-left: var(--cpd-space-3x);
margin-right: var(--cpd-space-2x);
padding-inline-start: var(--cpd-space-1-5x);
}
@@ -110,6 +110,17 @@ export const Condensed: Story = {
},
};
export const NoAvatar: Story = {
args: {
displayName: "Sally Sanderson",
userId: "@person-name:homeserver.com",
expanded: true,
open: true,
showAvatar: false,
},
parameters: Open.parameters,
};
export const Guest: Story = {
args: {
displayName: "Guest",
@@ -13,7 +13,7 @@ import userEvent from "@testing-library/user-event";
import * as stories from "./UserMenu.stories.tsx";
const { Default, LongerName, Condensed, Guest } = composeStories(stories);
const { Default, LongerName, Condensed, Guest, Open, NoAvatar } = composeStories(stories);
describe("UserMenu", () => {
it("renders a button", async () => {
@@ -38,4 +38,12 @@ describe("UserMenu", () => {
await userEvent.click(getByRole("button"));
expect(baseElement).toMatchSnapshot();
});
it("renders an open menu", async () => {
const { baseElement } = render(<Open />);
expect(baseElement).toMatchSnapshot();
});
it("renders a menu without an avatar", async () => {
const { baseElement } = render(<NoAvatar />);
expect(baseElement).toMatchSnapshot();
});
});
@@ -102,7 +102,7 @@ export declare interface UserMenuViewActions {
export type UserMenuViewProps = {
vm: ViewModel<UserMenuViewSnapshot, UserMenuViewActions>;
/**
* Class name for the container
* Class name for the trigger
*/
className?: string;
};
@@ -113,75 +113,92 @@ export function UserMenuView({ vm, className }: UserMenuViewProps): JSX.Element
const trigger = (
<button className={classNames(styles.triggerButton, className)} aria-label={_t("menus|user_menu|title")}>
<Avatar id={userId} name={displayName} type="round" size="36px" src={avatarUrl} />
</button>
);
// The menu should appear to the right of the avatar, over the displayname if the menu is expanded,
// so the display name goes outside the menu block in a wrapper.
return (
<div className={styles.wrapper}>
<Menu
open={open}
showTitle={false}
title={_t("menus|user_menu|title")}
trigger={trigger}
onOpenChange={vm.setOpen}
align="start"
side="right"
className={styles.container}
>
<section className={styles.profile}>
{showAvatar && <Avatar id={userId} name={displayName} type="round" size="64px" src={avatarUrl} />}
<Text className={styles.displayname} type="body" size="lg" weight="semibold" as="span">
{displayName}
</Text>
<Text data-testid="userId" size="md" as="span" type="body">
{userId}
</Text>
{manageAccountHref && (
<Button as="a" size="md" kind="tertiary" href={manageAccountHref} Icon={PopOutIcon}>
{_t("menus|user_menu|manage_account")}
</Button>
)}
{actions.createAccount && (
<Button
className={styles.createAccount}
size="md"
as="button"
kind="primary"
onClick={vm.createAccount}
>
{_t("menus|user_menu|create_an_account")}
</Button>
)}
{actions.signIn && (
<Text as="span" weight="medium">
{_t("menus|user_menu|got_an_account")}
<Link as="button" onClick={vm.signIn}>
{_t("menus|user_menu|sign_in")}
</Link>
</Text>
)}
</section>
<Separator />
<section className={styles.actions}>
{actions.openHomePage && (
<MenuItem Icon={HomeIcon} label={_t("user_menu|open_home")} onSelect={vm.openHomePage} />
)}
{actions.linkNewDevice && (
<MenuItem
Icon={DevicesIcon}
label={_t("user_menu|link_new_device")}
onSelect={vm.linkNewDevice}
/>
)}
{actions.openSecurity && (
<MenuItem Icon={LockIcon} label={_t("user_menu|open_security")} onSelect={vm.openSecurity} />
)}
{actions.openFeedback && (
<MenuItem
Icon={ChatProblemIcon}
label={_t("user_menu|open_feedback")}
onSelect={vm.openFeedback}
/>
)}
{actions.openSettings && (
<MenuItem
Icon={SettingsIcon}
label={_t("user_menu|open_settings")}
onSelect={vm.openSettings}
/>
)}
</section>
</Menu>
{expanded && (
<Text type="heading" size="sm" as="span" weight="semibold">
{displayName}
</Text>
)}
</button>
);
return (
<Menu
open={open}
showTitle={false}
title={_t("menus|user_menu|title")}
trigger={trigger}
onOpenChange={vm.setOpen}
align="start"
side="bottom"
className={styles.container}
>
<section className={styles.profile}>
{showAvatar && <Avatar id={userId} name={displayName} type="round" size="88px" src={avatarUrl} />}
<Text className={styles.displayname} type="heading" size="md" weight="semibold" as="span">
{displayName}
</Text>
<Text data-testid="userId" size="md" as="span" type="body">
{userId}
</Text>
{manageAccountHref && (
<Button as="a" size="md" kind="tertiary" href={manageAccountHref} Icon={PopOutIcon}>
{_t("menus|user_menu|manage_account")}
</Button>
)}
{actions.createAccount && (
<Button
className={styles.createAccount}
size="md"
as="button"
kind="primary"
onClick={vm.createAccount}
>
{_t("menus|user_menu|create_an_account")}
</Button>
)}
{actions.signIn && (
<Text as="span" weight="medium">
{_t("menus|user_menu|got_an_account")}
<Link as="button" onClick={vm.signIn}>
{_t("menus|user_menu|sign_in")}
</Link>
</Text>
)}
</section>
<Separator />
<section className={styles.actions}>
{actions.openHomePage && (
<MenuItem Icon={HomeIcon} label={_t("user_menu|open_home")} onSelect={vm.openHomePage} />
)}
{actions.linkNewDevice && (
<MenuItem Icon={DevicesIcon} label={_t("user_menu|link_new_device")} onSelect={vm.linkNewDevice} />
)}
{actions.openSecurity && (
<MenuItem Icon={LockIcon} label={_t("user_menu|open_security")} onSelect={vm.openSecurity} />
)}
{actions.openFeedback && (
<MenuItem Icon={ChatProblemIcon} label={_t("user_menu|open_feedback")} onSelect={vm.openFeedback} />
)}
{actions.openSettings && (
<MenuItem Icon={SettingsIcon} label={_t("user_menu|open_settings")} onSelect={vm.openSettings} />
)}
</section>
</Menu>
</div>
);
}
@@ -2,141 +2,16 @@
exports[`UserMenu > renders a button 1`] = `
<div>
<button
aria-expanded="false"
aria-haspopup="menu"
aria-label="User menu"
class="UserMenu-module_triggerButton"
data-state="closed"
id="radix-_r_0_"
type="button"
<div
class="UserMenu-module_wrapper"
>
<span
aria-label="@person-name:homeserver.com"
class="_avatar_va14e_8"
data-color="1"
data-type="round"
role="img"
style="--cpd-avatar-size: 36px;"
>
<img
alt=""
class="_image_va14e_43"
data-type="round"
height="36px"
loading="lazy"
referrerpolicy="no-referrer"
src="/static/element.png"
width="36px"
/>
</span>
<span
class="_typography_6v6n8_153 _font-heading-sm-semibold_6v6n8_93"
>
Sally Sanderson
</span>
</button>
</div>
`;
exports[`UserMenu > renders a button with a longer name 1`] = `
<div>
<button
aria-expanded="false"
aria-haspopup="menu"
aria-label="User menu"
class="UserMenu-module_triggerButton"
data-state="closed"
id="radix-_r_2_"
type="button"
>
<span
aria-label="@person-name:homeserver.com"
class="_avatar_va14e_8"
data-color="1"
data-type="round"
role="img"
style="--cpd-avatar-size: 36px;"
>
<img
alt=""
class="_image_va14e_43"
data-type="round"
height="36px"
loading="lazy"
referrerpolicy="no-referrer"
src="/static/element.png"
width="36px"
/>
</span>
<span
class="_typography_6v6n8_153 _font-heading-sm-semibold_6v6n8_93"
>
Sally Sanderson with a longer name
</span>
</button>
</div>
`;
exports[`UserMenu > renders a guest menu 1`] = `
<body>
<div>
<button
aria-expanded="false"
aria-haspopup="menu"
aria-label="User menu"
class="UserMenu-module_triggerButton"
data-state="closed"
id="radix-_r_8_"
type="button"
>
<span
aria-label="@guest:attendees.example.org"
class="_avatar_va14e_8"
data-color="4"
data-type="round"
role="img"
style="--cpd-avatar-size: 36px;"
>
<img
alt=""
class="_image_va14e_43"
data-type="round"
height="36px"
loading="lazy"
referrerpolicy="no-referrer"
src="/static/element.png"
width="36px"
/>
</span>
<span
class="_typography_6v6n8_153 _font-heading-sm-semibold_6v6n8_93"
>
Guest
</span>
</button>
</div>
</body>
`;
exports[`UserMenu > renders a menu 1`] = `
<body>
<div>
<button
aria-expanded="false"
aria-haspopup="menu"
aria-label="User menu"
class="UserMenu-module_triggerButton"
data-state="closed"
id="radix-_r_6_"
id="radix-_r_0_"
type="button"
>
<span
@@ -158,46 +33,828 @@ exports[`UserMenu > renders a menu 1`] = `
width="36px"
/>
</span>
</button>
<span
class="_typography_6v6n8_153 _font-heading-sm-semibold_6v6n8_93"
>
Sally Sanderson
</span>
</div>
</div>
`;
exports[`UserMenu > renders a button with a longer name 1`] = `
<div>
<div
class="UserMenu-module_wrapper"
>
<button
aria-expanded="false"
aria-haspopup="menu"
aria-label="User menu"
class="UserMenu-module_triggerButton"
data-state="closed"
id="radix-_r_2_"
type="button"
>
<span
aria-label="@person-name:homeserver.com"
class="_avatar_va14e_8"
data-color="1"
data-type="round"
role="img"
style="--cpd-avatar-size: 36px;"
>
<img
alt=""
class="_image_va14e_43"
data-type="round"
height="36px"
loading="lazy"
referrerpolicy="no-referrer"
src="/static/element.png"
width="36px"
/>
</span>
</button>
<span
class="_typography_6v6n8_153 _font-heading-sm-semibold_6v6n8_93"
>
Sally Sanderson with a longer name
</span>
</div>
</div>
`;
exports[`UserMenu > renders a guest menu 1`] = `
<body>
<div>
<div
class="UserMenu-module_wrapper"
>
<button
aria-expanded="false"
aria-haspopup="menu"
aria-label="User menu"
class="UserMenu-module_triggerButton"
data-state="closed"
id="radix-_r_8_"
type="button"
>
<span
aria-label="@guest:attendees.example.org"
class="_avatar_va14e_8"
data-color="4"
data-type="round"
role="img"
style="--cpd-avatar-size: 36px;"
>
<img
alt=""
class="_image_va14e_43"
data-type="round"
height="36px"
loading="lazy"
referrerpolicy="no-referrer"
src="/static/element.png"
width="36px"
/>
</span>
</button>
<span
class="_typography_6v6n8_153 _font-heading-sm-semibold_6v6n8_93"
>
Guest
</span>
</div>
</div>
</body>
`;
exports[`UserMenu > renders a menu 1`] = `
<body>
<div>
<div
class="UserMenu-module_wrapper"
>
<button
aria-expanded="false"
aria-haspopup="menu"
aria-label="User menu"
class="UserMenu-module_triggerButton"
data-state="closed"
id="radix-_r_6_"
type="button"
>
<span
aria-label="@person-name:homeserver.com"
class="_avatar_va14e_8"
data-color="1"
data-type="round"
role="img"
style="--cpd-avatar-size: 36px;"
>
<img
alt=""
class="_image_va14e_43"
data-type="round"
height="36px"
loading="lazy"
referrerpolicy="no-referrer"
src="/static/element.png"
width="36px"
/>
</span>
</button>
<span
class="_typography_6v6n8_153 _font-heading-sm-semibold_6v6n8_93"
>
Sally Sanderson
</span>
</button>
</div>
</div>
</body>
`;
exports[`UserMenu > renders a menu without an avatar 1`] = `
<body
data-scroll-locked="1"
style="pointer-events: none;"
>
<span
aria-hidden="true"
data-aria-hidden="true"
data-radix-focus-guard=""
style="outline: none; opacity: 0; position: fixed; pointer-events: none;"
tabindex="0"
/>
<div
aria-hidden="true"
data-aria-hidden="true"
>
<div
class="UserMenu-module_wrapper"
>
<button
aria-controls="radix-_r_i_"
aria-expanded="true"
aria-haspopup="menu"
aria-label="User menu"
class="UserMenu-module_triggerButton"
data-state="open"
id="radix-_r_h_"
type="button"
>
<span
aria-label="@person-name:homeserver.com"
class="_avatar_va14e_8"
data-color="1"
data-type="round"
role="img"
style="--cpd-avatar-size: 36px;"
>
<img
alt=""
class="_image_va14e_43"
data-type="round"
height="36px"
loading="lazy"
referrerpolicy="no-referrer"
src="/static/element.png"
width="36px"
/>
</span>
</button>
<span
class="_typography_6v6n8_153 _font-heading-sm-semibold_6v6n8_93"
>
Sally Sanderson
</span>
</div>
</div>
<div
data-radix-popper-content-wrapper=""
dir="ltr"
style="position: fixed; left: 0px; top: 0px; transform: translate(0px, -200%); min-width: max-content; --radix-popper-transform-origin: ; z-index: auto;"
>
<div
aria-label="User menu"
aria-labelledby="radix-_r_h_"
aria-orientation="vertical"
class="UserMenu-module_container _menu_1kl3y_8"
data-align="start"
data-orientation="vertical"
data-radix-menu-content=""
data-side="right"
data-state="open"
dir="ltr"
id="radix-_r_i_"
role="menu"
style="outline: none; --radix-dropdown-menu-content-transform-origin: var(--radix-popper-transform-origin); --radix-dropdown-menu-content-available-width: var(--radix-popper-available-width); --radix-dropdown-menu-content-available-height: var(--radix-popper-available-height); --radix-dropdown-menu-trigger-width: var(--radix-popper-anchor-width); --radix-dropdown-menu-trigger-height: var(--radix-popper-anchor-height); animation: auto ease 0s 1 normal none running none; pointer-events: auto;"
tabindex="-1"
>
<section
class="UserMenu-module_profile"
>
<span
class="_typography_6v6n8_153 _font-body-lg-semibold_6v6n8_74 UserMenu-module_displayname"
>
Sally Sanderson
</span>
<span
class="_typography_6v6n8_153 _font-body-md-regular_6v6n8_50"
data-testid="userId"
>
@person-name:homeserver.com
</span>
<a
class="_button_1nw83_8 _has-icon_1nw83_60"
data-kind="tertiary"
data-size="md"
href="#"
role="link"
tabindex="0"
>
<svg
aria-hidden="true"
fill="currentColor"
height="20"
viewBox="0 0 24 24"
width="20"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M5 3h6a1 1 0 1 1 0 2H5v14h14v-6a1 1 0 1 1 2 0v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2"
/>
<path
d="M15 3h5a1 1 0 0 1 1 1v5a1 1 0 1 1-2 0V6.414l-6.293 6.293a1 1 0 0 1-1.414-1.414L17.586 5H15a1 1 0 1 1 0-2"
/>
</svg>
Manage account
</a>
</section>
<div
class="_separator_13qwf_8"
data-kind="primary"
data-orientation="horizontal"
role="separator"
/>
<section
class="UserMenu-module_actions"
>
<button
class="_item_bym9p_8 _interactive_bym9p_26"
data-kind="primary"
data-orientation="vertical"
data-radix-collection-item=""
role="menuitem"
tabindex="-1"
>
<svg
aria-hidden="true"
class="_icon_bym9p_51"
fill="currentColor"
height="24"
viewBox="0 0 24 24"
width="24"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M3.5 20q-.625 0-1.062-.437A1.45 1.45 0 0 1 2 18.5q0-.625.438-1.062A1.45 1.45 0 0 1 3.5 17H4V6q0-.824.588-1.412A1.93 1.93 0 0 1 6 4h14q.424 0 .712.287Q21 4.576 21 5t-.288.713A.97.97 0 0 1 20 6H6v11h4.5q.624 0 1.063.438.437.437.437 1.062t-.437 1.063A1.45 1.45 0 0 1 10.5 20zM15 20a.97.97 0 0 1-.713-.288A.97.97 0 0 1 14 19V9q0-.424.287-.713A.97.97 0 0 1 15 8h6q.424 0 .712.287Q22 8.576 22 9v10q0 .424-.288.712A.97.97 0 0 1 21 20zm1-3h4v-7h-4z"
/>
</svg>
<span
class="_typography_6v6n8_153 _font-body-md-medium_6v6n8_60 _label_bym9p_35"
>
Link new device
</span>
<svg
aria-hidden="true"
class="_nav-hint_bym9p_60"
fill="currentColor"
height="24"
viewBox="8 0 8 24"
width="8"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M8.7 17.3a.95.95 0 0 1-.275-.7q0-.425.275-.7l3.9-3.9-3.9-3.9a.95.95 0 0 1-.275-.7q0-.425.275-.7a.95.95 0 0 1 .7-.275q.425 0 .7.275l4.6 4.6q.15.15.213.325.062.175.062.375t-.062.375a.9.9 0 0 1-.213.325l-4.6 4.6a.95.95 0 0 1-.7.275.95.95 0 0 1-.7-.275"
/>
</svg>
</button>
<button
class="_item_bym9p_8 _interactive_bym9p_26"
data-kind="primary"
data-orientation="vertical"
data-radix-collection-item=""
role="menuitem"
tabindex="-1"
>
<svg
aria-hidden="true"
class="_icon_bym9p_51"
fill="currentColor"
height="24"
viewBox="0 0 24 24"
width="24"
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 22zm0-2h12V10H6zM9 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>
<span
class="_typography_6v6n8_153 _font-body-md-medium_6v6n8_60 _label_bym9p_35"
>
Security & Privacy
</span>
<svg
aria-hidden="true"
class="_nav-hint_bym9p_60"
fill="currentColor"
height="24"
viewBox="8 0 8 24"
width="8"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M8.7 17.3a.95.95 0 0 1-.275-.7q0-.425.275-.7l3.9-3.9-3.9-3.9a.95.95 0 0 1-.275-.7q0-.425.275-.7a.95.95 0 0 1 .7-.275q.425 0 .7.275l4.6 4.6q.15.15.213.325.062.175.062.375t-.062.375a.9.9 0 0 1-.213.325l-4.6 4.6a.95.95 0 0 1-.7.275.95.95 0 0 1-.7-.275"
/>
</svg>
</button>
<button
class="_item_bym9p_8 _interactive_bym9p_26"
data-kind="primary"
data-orientation="vertical"
data-radix-collection-item=""
role="menuitem"
tabindex="-1"
>
<svg
aria-hidden="true"
class="_icon_bym9p_51"
fill="currentColor"
height="24"
viewBox="0 0 24 24"
width="24"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M12.713 16.713A.97.97 0 0 1 12 17a.97.97 0 0 1-.713-.288A.97.97 0 0 1 11 16q0-.424.287-.713A.97.97 0 0 1 12 15q.424 0 .713.287.287.288.287.713 0 .424-.287.712m0-3.999A.97.97 0 0 1 12 13a.97.97 0 0 1-.713-.287A.97.97 0 0 1 11 12V8q0-.424.287-.713A.97.97 0 0 1 12 7q.424 0 .713.287Q13 7.576 13 8v4q0 .424-.287.713"
/>
<path
d="M2.95 16.3a10.2 10.2 0 0 1-.712-2.1A10.2 10.2 0 0 1 2 12q0-2.075.788-3.9a10.1 10.1 0 0 1 2.137-3.175q1.35-1.35 3.175-2.137A9.7 9.7 0 0 1 12 2q2.075 0 3.9.788a10.1 10.1 0 0 1 3.175 2.137q1.35 1.35 2.137 3.175A9.7 9.7 0 0 1 22 12a9.7 9.7 0 0 1-.788 3.9 10.1 10.1 0 0 1-2.137 3.175q-1.35 1.35-3.175 2.137A9.7 9.7 0 0 1 12 22q-1.125 0-2.2-.238a10.2 10.2 0 0 1-2.1-.712L2.75 22.5a.94.94 0 0 1-1-.25.94.94 0 0 1-.25-1zm4.2 2.8a1 1 0 0 1 .275-.062q.15-.013.275-.013.225 0 .438.038.212.036.412.137a7.4 7.4 0 0 0 1.675.6Q11.1 20 12 20q3.35 0 5.675-2.325T20 12t-2.325-5.675T12 4 6.325 6.325 4 12q0 .9.2 1.775t.6 1.675q.176.325.188.688t-.088.712l-.95 3.2z"
/>
</svg>
<span
class="_typography_6v6n8_153 _font-body-md-medium_6v6n8_60 _label_bym9p_35"
>
Feedback
</span>
<svg
aria-hidden="true"
class="_nav-hint_bym9p_60"
fill="currentColor"
height="24"
viewBox="8 0 8 24"
width="8"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M8.7 17.3a.95.95 0 0 1-.275-.7q0-.425.275-.7l3.9-3.9-3.9-3.9a.95.95 0 0 1-.275-.7q0-.425.275-.7a.95.95 0 0 1 .7-.275q.425 0 .7.275l4.6 4.6q.15.15.213.325.062.175.062.375t-.062.375a.9.9 0 0 1-.213.325l-4.6 4.6a.95.95 0 0 1-.7.275.95.95 0 0 1-.7-.275"
/>
</svg>
</button>
<button
class="_item_bym9p_8 _interactive_bym9p_26"
data-kind="primary"
data-orientation="vertical"
data-radix-collection-item=""
role="menuitem"
tabindex="-1"
>
<svg
aria-hidden="true"
class="_icon_bym9p_51"
fill="currentColor"
height="24"
viewBox="0 0 24 24"
width="24"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M16 12a4 4 0 1 1-8 0 4 4 0 0 1 8 0m-2 0a2 2 0 1 0-4 0 2 2 0 0 0 4 0"
/>
<path
d="M11.312 2h1.376A2.31 2.31 0 0 1 15 4.312v.247l.002.003c.01.014.031.033.064.047.03.013.056.013.07.01h.002l.177-.177a2.31 2.31 0 0 1 3.27 0l.973.974a2.31 2.31 0 0 1 0 3.269l-.177.177v.003a.13.13 0 0 0 .01.07.15.15 0 0 0 .047.063l.003.002h.247A2.31 2.31 0 0 1 22 11.312v1.376A2.31 2.31 0 0 1 19.688 15h-.247l-.003.002a.15.15 0 0 0-.047.064.13.13 0 0 0-.01.07v.002l.177.177a2.31 2.31 0 0 1 0 3.27l-.974.973a2.31 2.31 0 0 1-3.269 0l-.177-.177h-.003a.13.13 0 0 0-.07.01.15.15 0 0 0-.063.047l-.002.003v.247A2.31 2.31 0 0 1 12.688 22h-1.376A2.31 2.31 0 0 1 9 19.688v-.247l-.002-.003a.15.15 0 0 0-.064-.047.13.13 0 0 0-.07-.01h-.002l-.177.177a2.31 2.31 0 0 1-3.27 0l-.973-.974a2.31 2.31 0 0 1 0-3.269l.177-.177v-.003a.14.14 0 0 0-.01-.07.15.15 0 0 0-.047-.063L4.559 15h-.247A2.31 2.31 0 0 1 2 12.688v-1.376A2.31 2.31 0 0 1 4.312 9h.247l.003-.002a.15.15 0 0 0 .047-.064.14.14 0 0 0 .01-.07v-.002l-.177-.177a2.31 2.31 0 0 1 0-3.27l.974-.973a2.31 2.31 0 0 1 3.269 0l.177.177h.003a.14.14 0 0 0 .07-.01.15.15 0 0 0 .063-.047L9 4.559v-.247A2.31 2.31 0 0 1 11.312 2M11 4.312v.257c0 .893-.59 1.593-1.299 1.887-.716.297-1.622.21-2.248-.418l-.182-.182a.31.31 0 0 0-.441 0l-.974.974a.31.31 0 0 0 0 .44l.182.183c.627.626.715 1.531.418 2.248C6.162 10.41 5.462 11 4.569 11h-.257a.31.31 0 0 0-.312.312v1.376c0 .172.14.312.312.312h.257c.893 0 1.593.59 1.887 1.299.297.716.21 1.622-.418 2.248l-.182.182a.31.31 0 0 0 0 .441l.974.973a.31.31 0 0 0 .44 0l.183-.181c.626-.627 1.532-.715 2.248-.418.709.294 1.299.994 1.299 1.887v.257c0 .172.14.312.312.312h1.376c.172 0 .312-.14.312-.312v-.257c0-.893.59-1.593 1.299-1.887.716-.297 1.622-.21 2.249.418l.181.181c.122.122.32.122.441 0l.973-.973a.31.31 0 0 0 0-.44l-.181-.183c-.627-.626-.715-1.532-.418-2.248.294-.709.994-1.299 1.887-1.299h.257c.172 0 .312-.14.312-.312v-1.376a.31.31 0 0 0-.312-.312h-.257c-.893 0-1.593-.59-1.887-1.299-.297-.717-.21-1.622.418-2.248l.181-.182a.31.31 0 0 0 0-.441l-.973-.974a.31.31 0 0 0-.44 0l-.183.182c-.626.627-1.532.715-2.248.418C13.59 6.162 13 5.462 13 4.569v-.257A.31.31 0 0 0 12.688 4h-1.376a.31.31 0 0 0-.312.312"
/>
</svg>
<span
class="_typography_6v6n8_153 _font-body-md-medium_6v6n8_60 _label_bym9p_35"
>
All settings
</span>
<svg
aria-hidden="true"
class="_nav-hint_bym9p_60"
fill="currentColor"
height="24"
viewBox="8 0 8 24"
width="8"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M8.7 17.3a.95.95 0 0 1-.275-.7q0-.425.275-.7l3.9-3.9-3.9-3.9a.95.95 0 0 1-.275-.7q0-.425.275-.7a.95.95 0 0 1 .7-.275q.425 0 .7.275l4.6 4.6q.15.15.213.325.062.175.062.375t-.062.375a.9.9 0 0 1-.213.325l-4.6 4.6a.95.95 0 0 1-.7.275.95.95 0 0 1-.7-.275"
/>
</svg>
</button>
</section>
</div>
</div>
<span
aria-hidden="true"
data-aria-hidden="true"
data-radix-focus-guard=""
style="outline: none; opacity: 0; position: fixed; pointer-events: none;"
tabindex="0"
/>
</body>
`;
exports[`UserMenu > renders an open menu 1`] = `
<body
data-scroll-locked="1"
style="pointer-events: none;"
>
<span
aria-hidden="true"
data-aria-hidden="true"
data-radix-focus-guard=""
style="outline: none; opacity: 0; position: fixed; pointer-events: none;"
tabindex="0"
/>
<div
aria-hidden="true"
data-aria-hidden="true"
>
<div
class="UserMenu-module_wrapper"
>
<button
aria-controls="radix-_r_b_"
aria-expanded="true"
aria-haspopup="menu"
aria-label="User menu"
class="UserMenu-module_triggerButton"
data-state="open"
id="radix-_r_a_"
type="button"
>
<span
aria-label="@person-name:homeserver.com"
class="_avatar_va14e_8"
data-color="1"
data-type="round"
role="img"
style="--cpd-avatar-size: 36px;"
>
<img
alt=""
class="_image_va14e_43"
data-type="round"
height="36px"
loading="lazy"
referrerpolicy="no-referrer"
src="/static/element.png"
width="36px"
/>
</span>
</button>
<span
class="_typography_6v6n8_153 _font-heading-sm-semibold_6v6n8_93"
>
Sally Sanderson with a longer name
</span>
</div>
</div>
<div
data-radix-popper-content-wrapper=""
dir="ltr"
style="position: fixed; left: 0px; top: 0px; transform: translate(0px, -200%); min-width: max-content; --radix-popper-transform-origin: ; z-index: auto;"
>
<div
aria-label="User menu"
aria-labelledby="radix-_r_a_"
aria-orientation="vertical"
class="UserMenu-module_container _menu_1kl3y_8"
data-align="start"
data-orientation="vertical"
data-radix-menu-content=""
data-side="right"
data-state="open"
dir="ltr"
id="radix-_r_b_"
role="menu"
style="outline: none; --radix-dropdown-menu-content-transform-origin: var(--radix-popper-transform-origin); --radix-dropdown-menu-content-available-width: var(--radix-popper-available-width); --radix-dropdown-menu-content-available-height: var(--radix-popper-available-height); --radix-dropdown-menu-trigger-width: var(--radix-popper-anchor-width); --radix-dropdown-menu-trigger-height: var(--radix-popper-anchor-height); animation: auto ease 0s 1 normal none running none; pointer-events: auto;"
tabindex="-1"
>
<section
class="UserMenu-module_profile"
>
<span
aria-label="@person-name:homeserver.com"
class="_avatar_va14e_8"
data-color="1"
data-type="round"
role="img"
style="--cpd-avatar-size: 64px;"
>
<img
alt=""
class="_image_va14e_43"
data-type="round"
height="64px"
loading="lazy"
referrerpolicy="no-referrer"
src="/static/element.png"
width="64px"
/>
</span>
<span
class="_typography_6v6n8_153 _font-body-lg-semibold_6v6n8_74 UserMenu-module_displayname"
>
Sally Sanderson with a longer name
</span>
<span
class="_typography_6v6n8_153 _font-body-md-regular_6v6n8_50"
data-testid="userId"
>
@person-name:homeserver.com
</span>
<a
class="_button_1nw83_8 _has-icon_1nw83_60"
data-kind="tertiary"
data-size="md"
href="#"
role="link"
tabindex="0"
>
<svg
aria-hidden="true"
fill="currentColor"
height="20"
viewBox="0 0 24 24"
width="20"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M5 3h6a1 1 0 1 1 0 2H5v14h14v-6a1 1 0 1 1 2 0v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2"
/>
<path
d="M15 3h5a1 1 0 0 1 1 1v5a1 1 0 1 1-2 0V6.414l-6.293 6.293a1 1 0 0 1-1.414-1.414L17.586 5H15a1 1 0 1 1 0-2"
/>
</svg>
Manage account
</a>
</section>
<div
class="_separator_13qwf_8"
data-kind="primary"
data-orientation="horizontal"
role="separator"
/>
<section
class="UserMenu-module_actions"
>
<button
class="_item_bym9p_8 _interactive_bym9p_26"
data-kind="primary"
data-orientation="vertical"
data-radix-collection-item=""
role="menuitem"
tabindex="-1"
>
<svg
aria-hidden="true"
class="_icon_bym9p_51"
fill="currentColor"
height="24"
viewBox="0 0 24 24"
width="24"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M3.5 20q-.625 0-1.062-.437A1.45 1.45 0 0 1 2 18.5q0-.625.438-1.062A1.45 1.45 0 0 1 3.5 17H4V6q0-.824.588-1.412A1.93 1.93 0 0 1 6 4h14q.424 0 .712.287Q21 4.576 21 5t-.288.713A.97.97 0 0 1 20 6H6v11h4.5q.624 0 1.063.438.437.437.437 1.062t-.437 1.063A1.45 1.45 0 0 1 10.5 20zM15 20a.97.97 0 0 1-.713-.288A.97.97 0 0 1 14 19V9q0-.424.287-.713A.97.97 0 0 1 15 8h6q.424 0 .712.287Q22 8.576 22 9v10q0 .424-.288.712A.97.97 0 0 1 21 20zm1-3h4v-7h-4z"
/>
</svg>
<span
class="_typography_6v6n8_153 _font-body-md-medium_6v6n8_60 _label_bym9p_35"
>
Link new device
</span>
<svg
aria-hidden="true"
class="_nav-hint_bym9p_60"
fill="currentColor"
height="24"
viewBox="8 0 8 24"
width="8"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M8.7 17.3a.95.95 0 0 1-.275-.7q0-.425.275-.7l3.9-3.9-3.9-3.9a.95.95 0 0 1-.275-.7q0-.425.275-.7a.95.95 0 0 1 .7-.275q.425 0 .7.275l4.6 4.6q.15.15.213.325.062.175.062.375t-.062.375a.9.9 0 0 1-.213.325l-4.6 4.6a.95.95 0 0 1-.7.275.95.95 0 0 1-.7-.275"
/>
</svg>
</button>
<button
class="_item_bym9p_8 _interactive_bym9p_26"
data-kind="primary"
data-orientation="vertical"
data-radix-collection-item=""
role="menuitem"
tabindex="-1"
>
<svg
aria-hidden="true"
class="_icon_bym9p_51"
fill="currentColor"
height="24"
viewBox="0 0 24 24"
width="24"
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 22zm0-2h12V10H6zM9 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>
<span
class="_typography_6v6n8_153 _font-body-md-medium_6v6n8_60 _label_bym9p_35"
>
Security & Privacy
</span>
<svg
aria-hidden="true"
class="_nav-hint_bym9p_60"
fill="currentColor"
height="24"
viewBox="8 0 8 24"
width="8"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M8.7 17.3a.95.95 0 0 1-.275-.7q0-.425.275-.7l3.9-3.9-3.9-3.9a.95.95 0 0 1-.275-.7q0-.425.275-.7a.95.95 0 0 1 .7-.275q.425 0 .7.275l4.6 4.6q.15.15.213.325.062.175.062.375t-.062.375a.9.9 0 0 1-.213.325l-4.6 4.6a.95.95 0 0 1-.7.275.95.95 0 0 1-.7-.275"
/>
</svg>
</button>
<button
class="_item_bym9p_8 _interactive_bym9p_26"
data-kind="primary"
data-orientation="vertical"
data-radix-collection-item=""
role="menuitem"
tabindex="-1"
>
<svg
aria-hidden="true"
class="_icon_bym9p_51"
fill="currentColor"
height="24"
viewBox="0 0 24 24"
width="24"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M12.713 16.713A.97.97 0 0 1 12 17a.97.97 0 0 1-.713-.288A.97.97 0 0 1 11 16q0-.424.287-.713A.97.97 0 0 1 12 15q.424 0 .713.287.287.288.287.713 0 .424-.287.712m0-3.999A.97.97 0 0 1 12 13a.97.97 0 0 1-.713-.287A.97.97 0 0 1 11 12V8q0-.424.287-.713A.97.97 0 0 1 12 7q.424 0 .713.287Q13 7.576 13 8v4q0 .424-.287.713"
/>
<path
d="M2.95 16.3a10.2 10.2 0 0 1-.712-2.1A10.2 10.2 0 0 1 2 12q0-2.075.788-3.9a10.1 10.1 0 0 1 2.137-3.175q1.35-1.35 3.175-2.137A9.7 9.7 0 0 1 12 2q2.075 0 3.9.788a10.1 10.1 0 0 1 3.175 2.137q1.35 1.35 2.137 3.175A9.7 9.7 0 0 1 22 12a9.7 9.7 0 0 1-.788 3.9 10.1 10.1 0 0 1-2.137 3.175q-1.35 1.35-3.175 2.137A9.7 9.7 0 0 1 12 22q-1.125 0-2.2-.238a10.2 10.2 0 0 1-2.1-.712L2.75 22.5a.94.94 0 0 1-1-.25.94.94 0 0 1-.25-1zm4.2 2.8a1 1 0 0 1 .275-.062q.15-.013.275-.013.225 0 .438.038.212.036.412.137a7.4 7.4 0 0 0 1.675.6Q11.1 20 12 20q3.35 0 5.675-2.325T20 12t-2.325-5.675T12 4 6.325 6.325 4 12q0 .9.2 1.775t.6 1.675q.176.325.188.688t-.088.712l-.95 3.2z"
/>
</svg>
<span
class="_typography_6v6n8_153 _font-body-md-medium_6v6n8_60 _label_bym9p_35"
>
Feedback
</span>
<svg
aria-hidden="true"
class="_nav-hint_bym9p_60"
fill="currentColor"
height="24"
viewBox="8 0 8 24"
width="8"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M8.7 17.3a.95.95 0 0 1-.275-.7q0-.425.275-.7l3.9-3.9-3.9-3.9a.95.95 0 0 1-.275-.7q0-.425.275-.7a.95.95 0 0 1 .7-.275q.425 0 .7.275l4.6 4.6q.15.15.213.325.062.175.062.375t-.062.375a.9.9 0 0 1-.213.325l-4.6 4.6a.95.95 0 0 1-.7.275.95.95 0 0 1-.7-.275"
/>
</svg>
</button>
<button
class="_item_bym9p_8 _interactive_bym9p_26"
data-kind="primary"
data-orientation="vertical"
data-radix-collection-item=""
role="menuitem"
tabindex="-1"
>
<svg
aria-hidden="true"
class="_icon_bym9p_51"
fill="currentColor"
height="24"
viewBox="0 0 24 24"
width="24"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M16 12a4 4 0 1 1-8 0 4 4 0 0 1 8 0m-2 0a2 2 0 1 0-4 0 2 2 0 0 0 4 0"
/>
<path
d="M11.312 2h1.376A2.31 2.31 0 0 1 15 4.312v.247l.002.003c.01.014.031.033.064.047.03.013.056.013.07.01h.002l.177-.177a2.31 2.31 0 0 1 3.27 0l.973.974a2.31 2.31 0 0 1 0 3.269l-.177.177v.003a.13.13 0 0 0 .01.07.15.15 0 0 0 .047.063l.003.002h.247A2.31 2.31 0 0 1 22 11.312v1.376A2.31 2.31 0 0 1 19.688 15h-.247l-.003.002a.15.15 0 0 0-.047.064.13.13 0 0 0-.01.07v.002l.177.177a2.31 2.31 0 0 1 0 3.27l-.974.973a2.31 2.31 0 0 1-3.269 0l-.177-.177h-.003a.13.13 0 0 0-.07.01.15.15 0 0 0-.063.047l-.002.003v.247A2.31 2.31 0 0 1 12.688 22h-1.376A2.31 2.31 0 0 1 9 19.688v-.247l-.002-.003a.15.15 0 0 0-.064-.047.13.13 0 0 0-.07-.01h-.002l-.177.177a2.31 2.31 0 0 1-3.27 0l-.973-.974a2.31 2.31 0 0 1 0-3.269l.177-.177v-.003a.14.14 0 0 0-.01-.07.15.15 0 0 0-.047-.063L4.559 15h-.247A2.31 2.31 0 0 1 2 12.688v-1.376A2.31 2.31 0 0 1 4.312 9h.247l.003-.002a.15.15 0 0 0 .047-.064.14.14 0 0 0 .01-.07v-.002l-.177-.177a2.31 2.31 0 0 1 0-3.27l.974-.973a2.31 2.31 0 0 1 3.269 0l.177.177h.003a.14.14 0 0 0 .07-.01.15.15 0 0 0 .063-.047L9 4.559v-.247A2.31 2.31 0 0 1 11.312 2M11 4.312v.257c0 .893-.59 1.593-1.299 1.887-.716.297-1.622.21-2.248-.418l-.182-.182a.31.31 0 0 0-.441 0l-.974.974a.31.31 0 0 0 0 .44l.182.183c.627.626.715 1.531.418 2.248C6.162 10.41 5.462 11 4.569 11h-.257a.31.31 0 0 0-.312.312v1.376c0 .172.14.312.312.312h.257c.893 0 1.593.59 1.887 1.299.297.716.21 1.622-.418 2.248l-.182.182a.31.31 0 0 0 0 .441l.974.973a.31.31 0 0 0 .44 0l.183-.181c.626-.627 1.532-.715 2.248-.418.709.294 1.299.994 1.299 1.887v.257c0 .172.14.312.312.312h1.376c.172 0 .312-.14.312-.312v-.257c0-.893.59-1.593 1.299-1.887.716-.297 1.622-.21 2.249.418l.181.181c.122.122.32.122.441 0l.973-.973a.31.31 0 0 0 0-.44l-.181-.183c-.627-.626-.715-1.532-.418-2.248.294-.709.994-1.299 1.887-1.299h.257c.172 0 .312-.14.312-.312v-1.376a.31.31 0 0 0-.312-.312h-.257c-.893 0-1.593-.59-1.887-1.299-.297-.717-.21-1.622.418-2.248l.181-.182a.31.31 0 0 0 0-.441l-.973-.974a.31.31 0 0 0-.44 0l-.183.182c-.626.627-1.532.715-2.248.418C13.59 6.162 13 5.462 13 4.569v-.257A.31.31 0 0 0 12.688 4h-1.376a.31.31 0 0 0-.312.312"
/>
</svg>
<span
class="_typography_6v6n8_153 _font-body-md-medium_6v6n8_60 _label_bym9p_35"
>
All settings
</span>
<svg
aria-hidden="true"
class="_nav-hint_bym9p_60"
fill="currentColor"
height="24"
viewBox="8 0 8 24"
width="8"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M8.7 17.3a.95.95 0 0 1-.275-.7q0-.425.275-.7l3.9-3.9-3.9-3.9a.95.95 0 0 1-.275-.7q0-.425.275-.7a.95.95 0 0 1 .7-.275q.425 0 .7.275l4.6 4.6q.15.15.213.325.062.175.062.375t-.062.375a.9.9 0 0 1-.213.325l-4.6 4.6a.95.95 0 0 1-.7.275.95.95 0 0 1-.7-.275"
/>
</svg>
</button>
</section>
</div>
</div>
<span
aria-hidden="true"
data-aria-hidden="true"
data-radix-focus-guard=""
style="outline: none; opacity: 0; position: fixed; pointer-events: none;"
tabindex="0"
/>
</body>
`;
exports[`UserMenu > renders condensed view 1`] = `
<div>
<button
aria-expanded="false"
aria-haspopup="menu"
aria-label="User menu"
class="UserMenu-module_triggerButton"
data-state="closed"
id="radix-_r_4_"
type="button"
<div
class="UserMenu-module_wrapper"
>
<span
aria-label="@person-name:homeserver.com"
class="_avatar_va14e_8"
data-color="1"
data-type="round"
role="img"
style="--cpd-avatar-size: 36px;"
<button
aria-expanded="false"
aria-haspopup="menu"
aria-label="User menu"
class="UserMenu-module_triggerButton"
data-state="closed"
id="radix-_r_4_"
type="button"
>
<img
alt=""
class="_image_va14e_43"
<span
aria-label="@person-name:homeserver.com"
class="_avatar_va14e_8"
data-color="1"
data-type="round"
height="36px"
loading="lazy"
referrerpolicy="no-referrer"
src="/static/element.png"
width="36px"
/>
</span>
</button>
role="img"
style="--cpd-avatar-size: 36px;"
>
<img
alt=""
class="_image_va14e_43"
data-type="round"
height="36px"
loading="lazy"
referrerpolicy="no-referrer"
src="/static/element.png"
width="36px"
/>
</span>
</button>
</div>
</div>
`;