Add notification menu stories and tests

This commit is contained in:
David Langley 2026-01-30 15:54:37 +00:00
parent 628f708ab5
commit 30d177f956
6 changed files with 725 additions and 137 deletions

View File

@ -0,0 +1,130 @@
/*
* 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 { fn } from "storybook/test";
import { userEvent, within } from "storybook/test";
import type { Meta, StoryObj } from "@storybook/react-vite";
import { RoomListItemNotificationMenu } from "./RoomListItemNotificationMenu";
import { type RoomListItemSnapshot, type RoomListItemActions } from "./RoomListItem";
import { useMockedViewModel } from "../../viewmodel";
import { defaultSnapshot } from "./default-snapshot";
import { RoomNotifState } from "./RoomNotifs";
type NotificationMenuProps = RoomListItemSnapshot & RoomListItemActions;
// Wrapper component that creates a mocked ViewModel
const NotificationMenuWrapper = ({
onOpenRoom,
onMarkAsRead,
onMarkAsUnread,
onToggleFavorite,
onToggleLowPriority,
onInvite,
onCopyRoomLink,
onLeaveRoom,
onSetRoomNotifState,
...rest
}: NotificationMenuProps): JSX.Element => {
const vm = useMockedViewModel(rest, {
onOpenRoom,
onMarkAsRead,
onMarkAsUnread,
onToggleFavorite,
onToggleLowPriority,
onInvite,
onCopyRoomLink,
onLeaveRoom,
onSetRoomNotifState,
});
return <RoomListItemNotificationMenu vm={vm} />;
};
const meta = {
title: "Room List/RoomListItem/NotificationMenu",
component: NotificationMenuWrapper,
tags: ["autodocs"],
decorators: [
(Story) => (
<div style={{ padding: "16px" }}>
<Story />
</div>
),
],
args: {
...defaultSnapshot,
onOpenRoom: fn(),
onMarkAsRead: fn(),
onMarkAsUnread: fn(),
onToggleFavorite: fn(),
onToggleLowPriority: fn(),
onInvite: fn(),
onCopyRoomLink: fn(),
onLeaveRoom: fn(),
onSetRoomNotifState: fn(),
},
} satisfies Meta<typeof NotificationMenuWrapper>;
export default meta;
type Story = StoryObj<typeof meta>;
export const Default: Story = {
args: {
roomNotifState: RoomNotifState.AllMessages,
},
};
export const Muted: Story = {
args: {
roomNotifState: RoomNotifState.Mute,
},
};
export const Open: Story = {
args: {
roomNotifState: RoomNotifState.AllMessages,
},
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
const trigger = canvas.getByRole("button", { name: "Notification options" });
await userEvent.click(trigger);
},
};
export const OpenMuted: Story = {
args: {
roomNotifState: RoomNotifState.Mute,
},
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
const trigger = canvas.getByRole("button", { name: "Notification options" });
await userEvent.click(trigger);
},
};
export const AllMessagesLoud: Story = {
args: {
roomNotifState: RoomNotifState.AllMessagesLoud,
},
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
const trigger = canvas.getByRole("button", { name: "Notification options" });
await userEvent.click(trigger);
},
};
export const MentionsOnly: Story = {
args: {
roomNotifState: RoomNotifState.MentionsOnly,
},
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
const trigger = canvas.getByRole("button", { name: "Notification options" });
await userEvent.click(trigger);
},
};

View File

@ -5,160 +5,80 @@
* Please see LICENSE files in the repository root for full details.
*/
import React, { type JSX } from "react";
import React from "react";
import { render, screen } from "@test-utils";
import userEvent from "@testing-library/user-event";
import { describe, it, expect, vi } from "vitest";
import { composeStories } from "@storybook/react-vite";
import { describe, it, expect } from "vitest";
import { RoomListItemNotificationMenu } from "./RoomListItemNotificationMenu";
import * as stories from "./RoomListItemNotificationMenu.stories";
import { RoomNotifState } from "./RoomNotifs";
import { useMockedViewModel } from "../../viewmodel";
import type { RoomListItemSnapshot } from "./RoomListItem";
import { defaultSnapshot } from "./default-snapshot";
describe("<RoomListItemNotificationMenu />", () => {
const mockCallbacks = {
onOpenRoom: vi.fn(),
onMarkAsRead: vi.fn(),
onMarkAsUnread: vi.fn(),
onToggleFavorite: vi.fn(),
onToggleLowPriority: vi.fn(),
onInvite: vi.fn(),
onCopyRoomLink: vi.fn(),
onLeaveRoom: vi.fn(),
onSetRoomNotifState: vi.fn(),
};
const { Default, Muted, Open, OpenMuted, AllMessagesLoud, MentionsOnly } = composeStories(stories);
const renderMenu = (roomNotifState: RoomNotifState = RoomNotifState.AllMessages): ReturnType<typeof render> => {
const TestComponent = (): JSX.Element => {
const vm = useMockedViewModel(
{
...defaultSnapshot,
showMoreOptionsMenu: false,
showNotificationMenu: true,
roomNotifState,
} as RoomListItemSnapshot,
mockCallbacks,
);
return <RoomListItemNotificationMenu vm={vm} />;
};
return render(<TestComponent />);
};
it("should render the notification menu button", () => {
renderMenu();
expect(screen.getByRole("button", { name: "Notification options" })).toBeInTheDocument();
describe("<RoomListItemNotificationMenu /> stories", () => {
it("renders Default story (closed, unmuted)", () => {
const { container } = render(<Default />);
expect(container).toMatchSnapshot();
});
it("should show muted icon when notifications are muted", () => {
renderMenu(RoomNotifState.Mute);
it("renders Muted story (closed, muted icon)", () => {
const { container } = render(<Muted />);
expect(container).toMatchSnapshot();
});
it("renders Open story", async () => {
const { container } = render(<Open />);
// Wait for play function to open the menu
await Open.play?.({ canvasElement: container });
expect(container).toMatchSnapshot();
});
it("renders OpenMuted story", async () => {
const { container } = render(<OpenMuted />);
// Wait for play function to open the menu
await OpenMuted.play?.({ canvasElement: container });
expect(container).toMatchSnapshot();
});
it("renders AllMessagesLoud story", async () => {
const { container } = render(<AllMessagesLoud />);
// Wait for play function to open the menu
await AllMessagesLoud.play?.({ canvasElement: container });
expect(container).toMatchSnapshot();
});
it("renders MentionsOnly story", async () => {
const { container } = render(<MentionsOnly />);
// Wait for play function to open the menu
await MentionsOnly.play?.({ canvasElement: container });
expect(container).toMatchSnapshot();
});
it("should show unmuted icon by default", () => {
render(<Default />);
const button = screen.getByRole("button", { name: "Notification options" });
expect(button.querySelector("svg")).toBeInTheDocument();
expect(button).toBeInTheDocument();
});
it("should open menu when clicked", async () => {
it("should show muted icon when muted", () => {
render(<Muted />);
const button = screen.getByRole("button", { name: "Notification options" });
expect(button).toBeInTheDocument();
});
it("should call onSetRoomNotifState when menu item is clicked", async () => {
const user = userEvent.setup();
renderMenu();
const button = screen.getByRole("button", { name: "Notification options" });
await user.click(button);
const { container } = render(<Open />);
await Open.play?.({ canvasElement: container });
// Menu should be open
expect(screen.getByRole("menu")).toBeInTheDocument();
});
it("should call onSetRoomNotifState with AllMessages when default settings selected", async () => {
const user = userEvent.setup();
renderMenu();
const button = screen.getByRole("button", { name: "Notification options" });
await user.click(button);
const defaultOption = screen.getByRole("menuitem", { name: "Match default settings" });
await user.click(defaultOption);
expect(mockCallbacks.onSetRoomNotifState).toHaveBeenCalledWith(RoomNotifState.AllMessages);
});
it("should call onSetRoomNotifState with AllMessagesLoud when all messages selected", async () => {
const user = userEvent.setup();
renderMenu();
const button = screen.getByRole("button", { name: "Notification options" });
await user.click(button);
const allMessagesOption = screen.getByRole("menuitem", { name: "All messages" });
await user.click(allMessagesOption);
expect(mockCallbacks.onSetRoomNotifState).toHaveBeenCalledWith(RoomNotifState.AllMessagesLoud);
});
it("should call onSetRoomNotifState with MentionsOnly when mentions and keywords selected", async () => {
const user = userEvent.setup();
renderMenu();
const button = screen.getByRole("button", { name: "Notification options" });
await user.click(button);
const mentionsOption = screen.getByRole("menuitem", { name: "Mentions and keywords" });
await user.click(mentionsOption);
expect(mockCallbacks.onSetRoomNotifState).toHaveBeenCalledWith(RoomNotifState.MentionsOnly);
});
it("should call onSetRoomNotifState with Mute when mute selected", async () => {
const user = userEvent.setup();
renderMenu();
const button = screen.getByRole("button", { name: "Notification options" });
await user.click(button);
// Click on "Mute room" option
const muteOption = screen.getByRole("menuitem", { name: "Mute room" });
await user.click(muteOption);
expect(mockCallbacks.onSetRoomNotifState).toHaveBeenCalledWith(RoomNotifState.Mute);
});
it("should show check mark next to selected option - AllMessage", async () => {
const user = userEvent.setup();
renderMenu(RoomNotifState.AllMessages);
const button = screen.getByRole("button", { name: "Notification options" });
await user.click(button);
const defaultOption = screen.getByRole("menuitem", { name: "Match default settings" });
expect(defaultOption).toHaveAttribute("aria-selected", "true");
});
it("should show check mark next to selected option - AllMessagesLoud", async () => {
const user = userEvent.setup();
renderMenu(RoomNotifState.AllMessagesLoud);
const button = screen.getByRole("button", { name: "Notification options" });
await user.click(button);
const allMessagesOption = screen.getByRole("menuitem", { name: "All messages" });
expect(allMessagesOption).toHaveAttribute("aria-selected", "true");
});
it("should show check mark next to selected option - MentionsOnly", async () => {
const user = userEvent.setup();
renderMenu(RoomNotifState.MentionsOnly);
const button = screen.getByRole("button", { name: "Notification options" });
await user.click(button);
const mentionsOption = screen.getByRole("menuitem", { name: "Mentions and keywords" });
expect(mentionsOption).toHaveAttribute("aria-selected", "true");
});
it("should show check mark next to selected option - Mute", async () => {
const user = userEvent.setup();
renderMenu(RoomNotifState.Mute);
const button = screen.getByRole("button", { name: "Notification options" });
await user.click(button);
const muteOption = screen.getByRole("menuitem", { name: "Mute room" });
expect(muteOption).toHaveAttribute("aria-selected", "true");
expect(Open.args.onSetRoomNotifState).toHaveBeenCalledWith(RoomNotifState.Mute);
});
});

View File

@ -0,0 +1,269 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
exports[`<RoomListItemNotificationMenu /> stories > renders AllMessagesLoud story 1`] = `
<div
aria-hidden="true"
data-aria-hidden="true"
>
<div
style="padding: 16px;"
>
<button
aria-controls="radix-_r_1b_"
aria-disabled="false"
aria-expanded="true"
aria-haspopup="menu"
aria-label="Notification options"
aria-labelledby="_r_1c_"
class="_icon-button_1215g_8"
data-kind="primary"
data-state="open"
id="radix-_r_1a_"
role="button"
style="--cpd-icon-button-size: 24px;"
tabindex="0"
type="button"
>
<div
class="_indicator-icon_147l5_17"
style="--cpd-icon-button-size: 100%;"
>
<svg
fill="currentColor"
height="1em"
viewBox="0 0 24 24"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M20.293 17.293c.63.63.184 1.707-.707 1.707H4.414c-.89 0-1.337-1.077-.707-1.707L5 16v-6s0-7 7-7 7 7 7 7v6zM12 22a2 2 0 0 1-2-2h4a2 2 0 0 1-2 2"
/>
</svg>
</div>
</button>
</div>
</div>
`;
exports[`<RoomListItemNotificationMenu /> stories > renders Default story (closed, unmuted) 1`] = `
<div>
<div
style="padding: 16px;"
>
<button
aria-disabled="false"
aria-expanded="false"
aria-haspopup="menu"
aria-label="Notification options"
aria-labelledby="_r_2_"
class="_icon-button_1215g_8"
data-kind="primary"
data-state="closed"
id="radix-_r_0_"
role="button"
style="--cpd-icon-button-size: 24px;"
tabindex="0"
type="button"
>
<div
class="_indicator-icon_147l5_17"
style="--cpd-icon-button-size: 100%;"
>
<svg
fill="currentColor"
height="1em"
viewBox="0 0 24 24"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M20.293 17.293c.63.63.184 1.707-.707 1.707H4.414c-.89 0-1.337-1.077-.707-1.707L5 16v-6s0-7 7-7 7 7 7 7v6zM12 22a2 2 0 0 1-2-2h4a2 2 0 0 1-2 2"
/>
</svg>
</div>
</button>
</div>
</div>
`;
exports[`<RoomListItemNotificationMenu /> stories > renders MentionsOnly story 1`] = `
<div
aria-hidden="true"
data-aria-hidden="true"
>
<div
style="padding: 16px;"
>
<button
aria-controls="radix-_r_1o_"
aria-disabled="false"
aria-expanded="true"
aria-haspopup="menu"
aria-label="Notification options"
aria-labelledby="_r_1p_"
class="_icon-button_1215g_8"
data-kind="primary"
data-state="open"
id="radix-_r_1n_"
role="button"
style="--cpd-icon-button-size: 24px;"
tabindex="0"
type="button"
>
<div
class="_indicator-icon_147l5_17"
style="--cpd-icon-button-size: 100%;"
>
<svg
fill="currentColor"
height="1em"
viewBox="0 0 24 24"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M20.293 17.293c.63.63.184 1.707-.707 1.707H4.414c-.89 0-1.337-1.077-.707-1.707L5 16v-6s0-7 7-7 7 7 7 7v6zM12 22a2 2 0 0 1-2-2h4a2 2 0 0 1-2 2"
/>
</svg>
</div>
</button>
</div>
</div>
`;
exports[`<RoomListItemNotificationMenu /> stories > renders Muted story (closed, muted icon) 1`] = `
<div>
<div
style="padding: 16px;"
>
<button
aria-disabled="false"
aria-expanded="false"
aria-haspopup="menu"
aria-label="Notification options"
aria-labelledby="_r_a_"
class="_icon-button_1215g_8"
data-kind="primary"
data-state="closed"
id="radix-_r_8_"
role="button"
style="--cpd-icon-button-size: 24px;"
tabindex="0"
type="button"
>
<div
class="_indicator-icon_147l5_17"
style="--cpd-icon-button-size: 100%;"
>
<svg
fill="currentColor"
height="1em"
viewBox="0 0 24 24"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="m4.917 2.083 17 17a1 1 0 0 1-1.414 1.414L19.006 19H4.414c-.89 0-1.337-1.077-.707-1.707L5 16v-6s0-2.034 1.096-3.91L3.504 3.498a1 1 0 0 1 1.414-1.414M19 13.35 9.136 3.484C9.93 3.181 10.874 3 12 3c7 0 7 7 7 7z"
/>
<path
d="M10 20h4a2 2 0 0 1-4 0"
/>
</svg>
</div>
</button>
</div>
</div>
`;
exports[`<RoomListItemNotificationMenu /> stories > renders Open story 1`] = `
<div
aria-hidden="true"
data-aria-hidden="true"
>
<div
style="padding: 16px;"
>
<button
aria-controls="radix-_r_h_"
aria-disabled="false"
aria-expanded="true"
aria-haspopup="menu"
aria-label="Notification options"
aria-labelledby="_r_i_"
class="_icon-button_1215g_8"
data-kind="primary"
data-state="open"
id="radix-_r_g_"
role="button"
style="--cpd-icon-button-size: 24px;"
tabindex="0"
type="button"
>
<div
class="_indicator-icon_147l5_17"
style="--cpd-icon-button-size: 100%;"
>
<svg
fill="currentColor"
height="1em"
viewBox="0 0 24 24"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M20.293 17.293c.63.63.184 1.707-.707 1.707H4.414c-.89 0-1.337-1.077-.707-1.707L5 16v-6s0-7 7-7 7 7 7 7v6zM12 22a2 2 0 0 1-2-2h4a2 2 0 0 1-2 2"
/>
</svg>
</div>
</button>
</div>
</div>
`;
exports[`<RoomListItemNotificationMenu /> stories > renders OpenMuted story 1`] = `
<div
aria-hidden="true"
data-aria-hidden="true"
>
<div
style="padding: 16px;"
>
<button
aria-controls="radix-_r_u_"
aria-disabled="false"
aria-expanded="true"
aria-haspopup="menu"
aria-label="Notification options"
aria-labelledby="_r_v_"
class="_icon-button_1215g_8"
data-kind="primary"
data-state="open"
id="radix-_r_t_"
role="button"
style="--cpd-icon-button-size: 24px;"
tabindex="0"
type="button"
>
<div
class="_indicator-icon_147l5_17"
style="--cpd-icon-button-size: 100%;"
>
<svg
fill="currentColor"
height="1em"
viewBox="0 0 24 24"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="m4.917 2.083 17 17a1 1 0 0 1-1.414 1.414L19.006 19H4.414c-.89 0-1.337-1.077-.707-1.707L5 16v-6s0-2.034 1.096-3.91L3.504 3.498a1 1 0 0 1 1.414-1.414M19 13.35 9.136 3.484C9.93 3.181 10.874 3 12 3c7 0 7 7 7 7z"
/>
<path
d="M10 20h4a2 2 0 0 1-4 0"
/>
</svg>
</div>
</button>
</div>
</div>
`;

View File

@ -0,0 +1,269 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
exports[`<RoomListItemNotificationMenu /> stories > renders AllMessagesLoud story 1`] = `
<div
aria-hidden="true"
data-aria-hidden="true"
>
<div
style="padding: 16px;"
>
<button
aria-controls="radix-_r_1b_"
aria-disabled="false"
aria-expanded="true"
aria-haspopup="menu"
aria-label="Notification options"
aria-labelledby="_r_1c_"
class="_icon-button_1215g_8"
data-kind="primary"
data-state="open"
id="radix-_r_1a_"
role="button"
style="--cpd-icon-button-size: 24px;"
tabindex="0"
type="button"
>
<div
class="_indicator-icon_147l5_17"
style="--cpd-icon-button-size: 100%;"
>
<svg
fill="currentColor"
height="1em"
viewBox="0 0 24 24"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M20.293 17.293c.63.63.184 1.707-.707 1.707H4.414c-.89 0-1.337-1.077-.707-1.707L5 16v-6s0-7 7-7 7 7 7 7v6zM12 22a2 2 0 0 1-2-2h4a2 2 0 0 1-2 2"
/>
</svg>
</div>
</button>
</div>
</div>
`;
exports[`<RoomListItemNotificationMenu /> stories > renders Default story (closed, unmuted) 1`] = `
<div>
<div
style="padding: 16px;"
>
<button
aria-disabled="false"
aria-expanded="false"
aria-haspopup="menu"
aria-label="Notification options"
aria-labelledby="_r_2_"
class="_icon-button_1215g_8"
data-kind="primary"
data-state="closed"
id="radix-_r_0_"
role="button"
style="--cpd-icon-button-size: 24px;"
tabindex="0"
type="button"
>
<div
class="_indicator-icon_147l5_17"
style="--cpd-icon-button-size: 100%;"
>
<svg
fill="currentColor"
height="1em"
viewBox="0 0 24 24"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M20.293 17.293c.63.63.184 1.707-.707 1.707H4.414c-.89 0-1.337-1.077-.707-1.707L5 16v-6s0-7 7-7 7 7 7 7v6zM12 22a2 2 0 0 1-2-2h4a2 2 0 0 1-2 2"
/>
</svg>
</div>
</button>
</div>
</div>
`;
exports[`<RoomListItemNotificationMenu /> stories > renders MentionsOnly story 1`] = `
<div
aria-hidden="true"
data-aria-hidden="true"
>
<div
style="padding: 16px;"
>
<button
aria-controls="radix-_r_1o_"
aria-disabled="false"
aria-expanded="true"
aria-haspopup="menu"
aria-label="Notification options"
aria-labelledby="_r_1p_"
class="_icon-button_1215g_8"
data-kind="primary"
data-state="open"
id="radix-_r_1n_"
role="button"
style="--cpd-icon-button-size: 24px;"
tabindex="0"
type="button"
>
<div
class="_indicator-icon_147l5_17"
style="--cpd-icon-button-size: 100%;"
>
<svg
fill="currentColor"
height="1em"
viewBox="0 0 24 24"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M20.293 17.293c.63.63.184 1.707-.707 1.707H4.414c-.89 0-1.337-1.077-.707-1.707L5 16v-6s0-7 7-7 7 7 7 7v6zM12 22a2 2 0 0 1-2-2h4a2 2 0 0 1-2 2"
/>
</svg>
</div>
</button>
</div>
</div>
`;
exports[`<RoomListItemNotificationMenu /> stories > renders Muted story (closed, muted icon) 1`] = `
<div>
<div
style="padding: 16px;"
>
<button
aria-disabled="false"
aria-expanded="false"
aria-haspopup="menu"
aria-label="Notification options"
aria-labelledby="_r_a_"
class="_icon-button_1215g_8"
data-kind="primary"
data-state="closed"
id="radix-_r_8_"
role="button"
style="--cpd-icon-button-size: 24px;"
tabindex="0"
type="button"
>
<div
class="_indicator-icon_147l5_17"
style="--cpd-icon-button-size: 100%;"
>
<svg
fill="currentColor"
height="1em"
viewBox="0 0 24 24"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="m4.917 2.083 17 17a1 1 0 0 1-1.414 1.414L19.006 19H4.414c-.89 0-1.337-1.077-.707-1.707L5 16v-6s0-2.034 1.096-3.91L3.504 3.498a1 1 0 0 1 1.414-1.414M19 13.35 9.136 3.484C9.93 3.181 10.874 3 12 3c7 0 7 7 7 7z"
/>
<path
d="M10 20h4a2 2 0 0 1-4 0"
/>
</svg>
</div>
</button>
</div>
</div>
`;
exports[`<RoomListItemNotificationMenu /> stories > renders Open story 1`] = `
<div
aria-hidden="true"
data-aria-hidden="true"
>
<div
style="padding: 16px;"
>
<button
aria-controls="radix-_r_h_"
aria-disabled="false"
aria-expanded="true"
aria-haspopup="menu"
aria-label="Notification options"
aria-labelledby="_r_i_"
class="_icon-button_1215g_8"
data-kind="primary"
data-state="open"
id="radix-_r_g_"
role="button"
style="--cpd-icon-button-size: 24px;"
tabindex="0"
type="button"
>
<div
class="_indicator-icon_147l5_17"
style="--cpd-icon-button-size: 100%;"
>
<svg
fill="currentColor"
height="1em"
viewBox="0 0 24 24"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M20.293 17.293c.63.63.184 1.707-.707 1.707H4.414c-.89 0-1.337-1.077-.707-1.707L5 16v-6s0-7 7-7 7 7 7 7v6zM12 22a2 2 0 0 1-2-2h4a2 2 0 0 1-2 2"
/>
</svg>
</div>
</button>
</div>
</div>
`;
exports[`<RoomListItemNotificationMenu /> stories > renders OpenMuted story 1`] = `
<div
aria-hidden="true"
data-aria-hidden="true"
>
<div
style="padding: 16px;"
>
<button
aria-controls="radix-_r_u_"
aria-disabled="false"
aria-expanded="true"
aria-haspopup="menu"
aria-label="Notification options"
aria-labelledby="_r_v_"
class="_icon-button_1215g_8"
data-kind="primary"
data-state="open"
id="radix-_r_t_"
role="button"
style="--cpd-icon-button-size: 24px;"
tabindex="0"
type="button"
>
<div
class="_indicator-icon_147l5_17"
style="--cpd-icon-button-size: 100%;"
>
<svg
fill="currentColor"
height="1em"
viewBox="0 0 24 24"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="m4.917 2.083 17 17a1 1 0 0 1-1.414 1.414L19.006 19H4.414c-.89 0-1.337-1.077-.707-1.707L5 16v-6s0-2.034 1.096-3.91L3.504 3.498a1 1 0 0 1 1.414-1.414M19 13.35 9.136 3.484C9.93 3.181 10.874 3 12 3c7 0 7 7 7 7z"
/>
<path
d="M10 20h4a2 2 0 0 1-4 0"
/>
</svg>
</div>
</button>
</div>
</div>
`;