diff --git a/src/Toast.test.tsx b/src/Toast.test.tsx
index e35e135c..b88fbee5 100644
--- a/src/Toast.test.tsx
+++ b/src/Toast.test.tsx
@@ -16,6 +16,7 @@ limitations under the License.
import { describe, expect, test, vi } from "vitest";
import { render, configure } from "@testing-library/react";
+import userEvent from "@testing-library/user-event";
import { Toast } from "../src/Toast";
import { withFakeTimers } from "./utils/test";
@@ -24,6 +25,9 @@ configure({
defaultHidden: true,
});
+// Test Explanation:
+// This test the toast. We need to use { document: window.document } because the toast listens
+// for user input on `window`.
describe("Toast", () => {
test("renders", () => {
const { queryByRole } = render(
@@ -40,6 +44,33 @@ describe("Toast", () => {
expect(getByRole("dialog")).toMatchSnapshot();
});
+ test("dismisses when Esc is pressed", async () => {
+ const user = userEvent.setup({ document: window.document });
+ const onDismiss = vi.fn();
+ const { debug } = render(
+
+ Hello world!
+ ,
+ );
+ debug();
+ await user.keyboard("[Escape]");
+ expect(onDismiss).toHaveBeenCalled();
+ });
+
+ test("dismisses when background is clicked", async () => {
+ const user = userEvent.setup();
+ const onDismiss = vi.fn();
+ const { getByRole, unmount } = render(
+
+ Hello world!
+ ,
+ );
+ const background = getByRole("dialog").previousSibling! as Element;
+ await user.click(background);
+ expect(onDismiss).toHaveBeenCalled();
+ unmount();
+ });
+
test("dismisses itself after the specified timeout", () => {
withFakeTimers(() => {
const onDismiss = vi.fn();
diff --git a/src/Tooltip.module.css b/src/Tooltip.module.css
deleted file mode 100644
index 07219f75..00000000
--- a/src/Tooltip.module.css
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
-Copyright 2022 New Vector Ltd
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-.tooltip {
- background-color: var(--cpd-color-bg-subtle-secondary);
- flex-direction: row;
- justify-content: center;
- align-items: center;
- padding: 10px;
- color: var(--cpd-color-text-primary);
- border-radius: 8px;
- max-width: 135px;
- width: max-content;
- font-size: var(--font-size-caption);
- font-weight: 500;
- text-align: center;
-}
diff --git a/src/Tooltip.tsx b/src/Tooltip.tsx
deleted file mode 100644
index adaa18b3..00000000
--- a/src/Tooltip.tsx
+++ /dev/null
@@ -1,118 +0,0 @@
-/*
-Copyright 2022 New Vector Ltd
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-import {
- ForwardedRef,
- forwardRef,
- ReactElement,
- ReactNode,
- useRef,
-} from "react";
-import {
- TooltipTriggerState,
- useTooltipTriggerState,
-} from "@react-stately/tooltip";
-import { FocusableProvider } from "@react-aria/focus";
-import { useTooltipTrigger, useTooltip } from "@react-aria/tooltip";
-import { mergeProps, useObjectRef } from "@react-aria/utils";
-import classNames from "classnames";
-import { OverlayContainer, useOverlayPosition } from "@react-aria/overlays";
-import { Placement } from "@react-types/overlays";
-
-import styles from "./Tooltip.module.css";
-
-interface TooltipProps {
- className?: string;
- state: TooltipTriggerState;
- children: ReactNode;
-}
-
-const Tooltip = forwardRef
(
- (
- { state, className, children, ...rest }: TooltipProps,
- ref: ForwardedRef,
- ) => {
- const { tooltipProps } = useTooltip(rest, state);
-
- return (
-
- {children}
-
- );
- },
-);
-
-Tooltip.displayName = "Tooltip";
-
-interface TooltipTriggerProps {
- children: ReactElement;
- placement?: Placement;
- delay?: number;
- tooltip: () => string;
-}
-
-export const TooltipTrigger = forwardRef(
- (
- { children, placement, tooltip, ...rest }: TooltipTriggerProps,
- ref: ForwardedRef,
- ) => {
- const tooltipTriggerProps = { delay: 250, ...rest };
- const tooltipState = useTooltipTriggerState(tooltipTriggerProps);
- const triggerRef = useObjectRef(ref);
- const overlayRef = useRef(null);
- const { triggerProps, tooltipProps } = useTooltipTrigger(
- tooltipTriggerProps,
- tooltipState,
- triggerRef,
- );
-
- const { overlayProps } = useOverlayPosition({
- placement: placement || "top",
- targetRef: triggerRef,
- overlayRef,
- isOpen: tooltipState.isOpen,
- offset: 12,
- });
-
- return (
-
- (
- children.props,
- rest,
- )}
- />
- {tooltipState.isOpen && (
-
-
- {tooltip()}
-
-
- )}
-
- );
- },
-);
-
-TooltipTrigger.displayName = "TooltipTrigger";
diff --git a/src/UserMenu.module.css b/src/UserMenu.module.css
index 575b71b9..d4e06037 100644
--- a/src/UserMenu.module.css
+++ b/src/UserMenu.module.css
@@ -21,6 +21,14 @@ limitations under the License.
flex-shrink: 0;
}
+.userButton {
+ appearance: none;
+ background: none;
+ border: none;
+ margin: 0;
+ cursor: pointer;
+}
+
.userButton svg * {
fill: var(--cpd-color-icon-primary);
}
diff --git a/src/UserMenu.tsx b/src/UserMenu.tsx
index 7cab4575..55ccc2c0 100644
--- a/src/UserMenu.tsx
+++ b/src/UserMenu.tsx
@@ -14,21 +14,17 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
-import { FC, ReactNode, useCallback, useMemo } from "react";
-import { Item } from "@react-stately/collections";
+import { FC, useMemo, useState } from "react";
import { useLocation } from "react-router-dom";
import { useTranslation } from "react-i18next";
+import { Menu, MenuItem } from "@vector-im/compound-web";
-import { Button, LinkButton } from "./button";
-import { PopoverMenuTrigger } from "./popover/PopoverMenu";
-import { Menu } from "./Menu";
-import { TooltipTrigger } from "./Tooltip";
+import { LinkButton } from "./button";
import { Avatar, Size } from "./Avatar";
import UserIcon from "./icons/User.svg?react";
import SettingsIcon from "./icons/Settings.svg?react";
import LoginIcon from "./icons/Login.svg?react";
import LogoutIcon from "./icons/Logout.svg?react";
-import { Body } from "./typography/Typography";
import styles from "./UserMenu.module.css";
interface Props {
@@ -91,7 +87,7 @@ export const UserMenu: FC = ({
return arr;
}, [isAuthenticated, isPasswordlessUser, displayName, preventNavigation, t]);
- const tooltip = useCallback(() => t("common.profile"), [t]);
+ const [open, setOpen] = useState(false);
if (!isAuthenticated) {
return (
@@ -102,10 +98,15 @@ export const UserMenu: FC = ({
}
return (
-
-
-
-
- {
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
- (props: any): ReactNode => (
-
- )
+
}
-
+ >
+ {items.map(({ key, icon: Icon, label, dataTestid }) => (
+