diff --git a/site/src/components/deprecated/Popover/Popover.stories.tsx b/site/src/components/deprecated/Popover/Popover.stories.tsx deleted file mode 100644 index 8661044c51..0000000000 --- a/site/src/components/deprecated/Popover/Popover.stories.tsx +++ /dev/null @@ -1,57 +0,0 @@ -import Button from "@mui/material/Button"; -import type { Meta, StoryObj } from "@storybook/react-vite"; -import { expect, screen, userEvent, waitFor, within } from "storybook/test"; -import { Popover, PopoverContent, PopoverTrigger } from "./Popover"; - -const meta: Meta = { - title: "components/PopoverDeprecated", - component: Popover, -}; - -export default meta; -type Story = StoryObj; - -const content = ` -According to all known laws of aviation, there is no way a bee should be able to fly. -Its wings are too small to get its fat little body off the ground. The bee, of course, -flies anyway because bees don't care what humans think is impossible. -`; - -export const Example: Story = { - args: { - children: ( - <> - - - - {content} - - ), - }, - play: async ({ canvasElement, step }) => { - const canvas = within(canvasElement); - - await step("click to open", async () => { - await userEvent.click(canvas.getByRole("button")); - await waitFor(() => - expect( - screen.getByText(/according to all known laws/i), - ).toBeInTheDocument(), - ); - }); - }, -}; - -export const Horizontal: Story = { - args: { - children: ( - <> - - - - {content} - - ), - }, - play: Example.play, -}; diff --git a/site/src/components/deprecated/Popover/Popover.tsx b/site/src/components/deprecated/Popover/Popover.tsx deleted file mode 100644 index f4ba5852c4..0000000000 --- a/site/src/components/deprecated/Popover/Popover.tsx +++ /dev/null @@ -1,245 +0,0 @@ -import MuiPopover, { - type PopoverProps as MuiPopoverProps, - // biome-ignore lint/style/noRestrictedImports: This is the base component that our custom popover is based on -} from "@mui/material/Popover"; -import { - cloneElement, - createContext, - type FC, - type HTMLAttributes, - type PointerEvent, - type PointerEventHandler, - type ReactElement, - type ReactNode, - type RefObject, - useContext, - useEffect, - useId, - useRef, - useState, -} from "react"; - -type TriggerMode = "hover" | "click"; - -type TriggerRef = RefObject; - -// Have to append ReactNode type to satisfy React's cloneElement function. It -// has absolutely no bearing on what happens at runtime -type TriggerElement = ReactNode & - ReactElement<{ - ref: TriggerRef; - onClick?: () => void; - }>; - -type PopoverContextValue = { - id: string; - open: boolean; - setOpen: (open: boolean) => void; - triggerRef: TriggerRef; - mode: TriggerMode; -}; - -const PopoverContext = createContext( - undefined, -); - -type BasePopoverProps = { - children: ReactNode; - mode?: TriggerMode; -}; - -// By separating controlled and uncontrolled props, we achieve more accurate -// type inference. -type UncontrolledPopoverProps = BasePopoverProps & { - open?: undefined; - onOpenChange?: undefined; -}; - -type ControlledPopoverProps = BasePopoverProps & { - open: boolean; - onOpenChange: (open: boolean) => void; -}; - -type PopoverProps = UncontrolledPopoverProps | ControlledPopoverProps; - -/** @deprecated prefer `components.Popover` */ -export const Popover: FC = (props) => { - const hookId = useId(); - const [uncontrolledOpen, setUncontrolledOpen] = useState(false); - const triggerRef: TriggerRef = useRef(null); - - // Helps makes sure that popovers close properly when the user switches to - // a different tab. This won't help with controlled instances of the - // component, but this is basically the most we can do from here - useEffect(() => { - const closeOnTabSwitch = () => setUncontrolledOpen(false); - window.addEventListener("blur", closeOnTabSwitch); - return () => window.removeEventListener("blur", closeOnTabSwitch); - }, []); - - const value: PopoverContextValue = { - triggerRef, - id: `${hookId}-popover`, - mode: props.mode ?? "click", - open: props.open ?? uncontrolledOpen, - setOpen: props.onOpenChange ?? setUncontrolledOpen, - }; - - return ( - - {props.children} - - ); -}; - -export const usePopover = () => { - const context = useContext(PopoverContext); - if (!context) { - throw new Error( - "Popover compound components cannot be rendered outside the Popover component", - ); - } - return context; -}; - -type PopoverTriggerRenderProps = Readonly<{ - isOpen: boolean; -}>; - -type PopoverTriggerProps = Readonly< - Omit, "children"> & { - children: - | TriggerElement - | ((props: PopoverTriggerRenderProps) => TriggerElement); - } ->; - -/** @deprecated prefer `components.Popover.PopoverTrigger` */ -export const PopoverTrigger: FC = (props) => { - const popover = usePopover(); - const { children, onClick, onPointerEnter, onPointerLeave, ...elementProps } = - props; - - const clickProps = { - onClick: (event: PointerEvent) => { - popover.setOpen(true); - onClick?.(event); - }, - }; - - const hoverProps = { - onPointerEnter: (event: PointerEvent) => { - popover.setOpen(true); - onPointerEnter?.(event); - }, - onPointerLeave: (event: PointerEvent) => { - popover.setOpen(false); - onPointerLeave?.(event); - }, - }; - - const evaluatedChildren = - typeof children === "function" - ? children({ isOpen: popover.open }) - : children; - - return cloneElement(evaluatedChildren, { - ...elementProps, - ...(popover.mode === "click" ? clickProps : hoverProps), - // @ts-expect-error I would usually not hack around this, but this component - // is going to be deleted imminently. - "aria-haspopup": true, - "aria-owns": popover.id, - "aria-expanded": popover.open, - ref: popover.triggerRef, - }); -}; - -type Horizontal = "left" | "right"; - -type PopoverContentProps = Omit< - MuiPopoverProps, - "open" | "onClose" | "anchorEl" -> & { - horizontal?: Horizontal; -}; - -/** @deprecated prefer `components.Popover.PopoverContent` */ -export const PopoverContent: FC = ({ - horizontal = "left", - onPointerEnter, - onPointerLeave, - ...popoverProps -}) => { - const popover = usePopover(); - const hoverMode = popover.mode === "hover"; - - return ( - popover.setOpen(false)} - anchorEl={popover.triggerRef.current} - /> - ); -}; - -const modeProps = ( - popover: PopoverContextValue, - externalOnPointerEnter: PointerEventHandler | undefined, - externalOnPointerLeave: PointerEventHandler | undefined, -) => { - if (popover.mode === "hover") { - return { - onPointerEnter: (event: PointerEvent) => { - popover.setOpen(true); - externalOnPointerEnter?.(event); - }, - onPointerLeave: (event: PointerEvent) => { - popover.setOpen(false); - externalOnPointerLeave?.(event); - }, - }; - } - - return {}; -}; - -const horizontalProps = (horizontal: Horizontal) => { - if (horizontal === "right") { - return { - anchorOrigin: { - vertical: "bottom", - horizontal: "right", - }, - transformOrigin: { - vertical: "top", - horizontal: "right", - }, - } as const; - } - - return { - anchorOrigin: { - vertical: "bottom", - horizontal: "left", - }, - } as const; -}; diff --git a/site/src/modules/dashboard/Navbar/UserDropdown/UserDropdown.stories.tsx b/site/src/modules/dashboard/Navbar/UserDropdown/UserDropdown.stories.tsx index c5025a3f4d..1eb86cb9cc 100644 --- a/site/src/modules/dashboard/Navbar/UserDropdown/UserDropdown.stories.tsx +++ b/site/src/modules/dashboard/Navbar/UserDropdown/UserDropdown.stories.tsx @@ -30,8 +30,8 @@ const Example: Story = { await step("click to open", async () => { await userEvent.click(canvas.getByRole("button")); - await waitFor(() => - expect(screen.getByText(/v2\.\d+\.\d+/i)).toBeInTheDocument(), + await waitFor(async () => + expect(await screen.findByText(/v2\.\d+\.\d+/i)).toBeInTheDocument(), ); }); }, diff --git a/site/src/pages/DeploymentSettingsPage/AppearanceSettingsPage/AppearanceSettingsPageView.tsx b/site/src/pages/DeploymentSettingsPage/AppearanceSettingsPage/AppearanceSettingsPageView.tsx index 010c7a999e..950fd64f83 100644 --- a/site/src/pages/DeploymentSettingsPage/AppearanceSettingsPage/AppearanceSettingsPageView.tsx +++ b/site/src/pages/DeploymentSettingsPage/AppearanceSettingsPage/AppearanceSettingsPageView.tsx @@ -7,17 +7,18 @@ import { PremiumBadge, } from "components/Badges/Badges"; import { Button } from "components/Button/Button"; -import { - Popover, - PopoverContent, - PopoverTrigger, -} from "components/deprecated/Popover/Popover"; import { PopoverPaywall } from "components/Paywall/PopoverPaywall"; import { SettingsHeader, SettingsHeaderDescription, SettingsHeaderTitle, } from "components/SettingsHeader/SettingsHeader"; +import { + Tooltip, + TooltipContent, + TooltipProvider, + TooltipTrigger, +} from "components/Tooltip/Tooltip"; import { useFormik } from "formik"; import type { FC } from "react"; import { getFormHelpers } from "utils/formUtils"; @@ -66,25 +67,31 @@ export const AppearanceSettingsPageView: FC< - - {isEntitled && !isPremium ? ( - - ) : ( - - - - - - )} + + + {isEntitled && !isPremium ? ( + + ) : ( + + + + + + )} - - - - + + + + +
= { args: { organizationSyncSettings: MockOrganizationSyncSettings2, claimFieldValues: Object.keys(MockOrganizationSyncSettings2.mapping), - organizations: [MockOrganization, MockOrganization2], + organizations: [MockOrganization, MockOrganization2, MockOrganization3], error: undefined, }, }; diff --git a/site/src/pages/DeploymentSettingsPage/IdpOrgSyncPage/OrganizationPills.tsx b/site/src/pages/DeploymentSettingsPage/IdpOrgSyncPage/OrganizationPills.tsx index 030e3889ca..3b73d0199a 100644 --- a/site/src/pages/DeploymentSettingsPage/IdpOrgSyncPage/OrganizationPills.tsx +++ b/site/src/pages/DeploymentSettingsPage/IdpOrgSyncPage/OrganizationPills.tsx @@ -1,10 +1,10 @@ -import { useTheme } from "@emotion/react"; -import { - Popover, - PopoverContent, - PopoverTrigger, -} from "components/deprecated/Popover/Popover"; import { Pill } from "components/Pill/Pill"; +import { + Tooltip, + TooltipContent, + TooltipProvider, + TooltipTrigger, +} from "components/Tooltip/Tooltip"; import type { FC } from "react"; import { cn } from "utils/cn"; import { isUUID } from "utils/uuid"; @@ -25,10 +25,10 @@ export const OrganizationPills: FC = ({
{orgs.length > 0 ? ( {orgs[0].name} @@ -46,58 +46,37 @@ interface OverflowPillProps { } const OverflowPillList: FC = ({ organizations }) => { - const theme = useTheme(); - return ( - - - - +{organizations.length} - - + + + + + +{organizations.length} + + - -
    - {organizations.map((organization) => ( -
  • - - {organization.name} - -
  • - ))} -
-
-
+ +
    + {organizations.map((organization) => ( +
  • + + {organization.name} + +
  • + ))} +
+
+ + ); }; diff --git a/site/src/pages/DeploymentSettingsPage/ObservabilitySettingsPage/ObservabilitySettingsPageView.stories.tsx b/site/src/pages/DeploymentSettingsPage/ObservabilitySettingsPage/ObservabilitySettingsPageView.stories.tsx index 2fb5af8d75..f52def7cd6 100644 --- a/site/src/pages/DeploymentSettingsPage/ObservabilitySettingsPage/ObservabilitySettingsPageView.stories.tsx +++ b/site/src/pages/DeploymentSettingsPage/ObservabilitySettingsPage/ObservabilitySettingsPageView.stories.tsx @@ -52,3 +52,5 @@ export default meta; type Story = StoryObj; export const Page: Story = {}; + +export const Premium: Story = { args: { isPremium: true } }; diff --git a/site/src/pages/DeploymentSettingsPage/ObservabilitySettingsPage/ObservabilitySettingsPageView.tsx b/site/src/pages/DeploymentSettingsPage/ObservabilitySettingsPage/ObservabilitySettingsPageView.tsx index cd152293e9..011db7f954 100644 --- a/site/src/pages/DeploymentSettingsPage/ObservabilitySettingsPage/ObservabilitySettingsPageView.tsx +++ b/site/src/pages/DeploymentSettingsPage/ObservabilitySettingsPage/ObservabilitySettingsPageView.tsx @@ -4,11 +4,6 @@ import { EnterpriseBadge, PremiumBadge, } from "components/Badges/Badges"; -import { - Popover, - PopoverContent, - PopoverTrigger, -} from "components/deprecated/Popover/Popover"; import { PopoverPaywall } from "components/Paywall/PopoverPaywall"; import { SettingsHeader, @@ -17,6 +12,12 @@ import { SettingsHeaderTitle, } from "components/SettingsHeader/SettingsHeader"; import { Stack } from "components/Stack/Stack"; +import { + Tooltip, + TooltipContent, + TooltipProvider, + TooltipTrigger, +} from "components/Tooltip/Tooltip"; import type { FC } from "react"; import { deploymentGroupHasParent } from "utils/deployOptions"; import { docs } from "utils/docs"; @@ -52,25 +53,31 @@ export const ObservabilitySettingsPageView: FC< - - {featureAuditLogEnabled && !isPremium ? ( - - ) : ( - - - - - - )} + + + {featureAuditLogEnabled && !isPremium ? ( + + ) : ( + + + + + + )} - - - - + + + + +
diff --git a/site/src/pages/OrganizationSettingsPage/CreateOrganizationPageView.tsx b/site/src/pages/OrganizationSettingsPage/CreateOrganizationPageView.tsx index 2b1902646f..6b87ae2606 100644 --- a/site/src/pages/OrganizationSettingsPage/CreateOrganizationPageView.tsx +++ b/site/src/pages/OrganizationSettingsPage/CreateOrganizationPageView.tsx @@ -5,15 +5,16 @@ import { ErrorAlert } from "components/Alert/ErrorAlert"; import { Badges, PremiumBadge } from "components/Badges/Badges"; import { Button } from "components/Button/Button"; import { ChooseOne, Cond } from "components/Conditionals/ChooseOne"; -import { - Popover, - PopoverContent, - PopoverTrigger, -} from "components/deprecated/Popover/Popover"; import { IconField } from "components/IconField/IconField"; import { Paywall } from "components/Paywall/Paywall"; import { PopoverPaywall } from "components/Paywall/PopoverPaywall"; import { Spinner } from "components/Spinner/Spinner"; +import { + Tooltip, + TooltipContent, + TooltipProvider, + TooltipTrigger, +} from "components/Tooltip/Tooltip"; import { useFormik } from "formik"; import { ArrowLeft } from "lucide-react"; import type { FC } from "react"; @@ -81,23 +82,29 @@ export const CreateOrganizationPageView: FC< )} - - {isEntitled && ( - - - - - - )} + + + {isEntitled && ( + + + + + + )} - - - - + + + + +
diff --git a/site/src/pages/OrganizationSettingsPage/CustomRolesPage/PermissionPillsList.tsx b/site/src/pages/OrganizationSettingsPage/CustomRolesPage/PermissionPillsList.tsx index 11071e0dab..fea0dff643 100644 --- a/site/src/pages/OrganizationSettingsPage/CustomRolesPage/PermissionPillsList.tsx +++ b/site/src/pages/OrganizationSettingsPage/CustomRolesPage/PermissionPillsList.tsx @@ -1,12 +1,13 @@ import { type Interpolation, type Theme, useTheme } from "@emotion/react"; import Stack from "@mui/material/Stack"; import type { Permission } from "api/typesGenerated"; -import { - Popover, - PopoverContent, - PopoverTrigger, -} from "components/deprecated/Popover/Popover"; import { Pill } from "components/Pill/Pill"; +import { + Tooltip, + TooltipContent, + TooltipProvider, + TooltipTrigger, +} from "components/Tooltip/Tooltip"; import type { FC } from "react"; function getUniqueResourceTypes(jsonObject: readonly Permission[]) { @@ -76,52 +77,34 @@ const OverflowPermissionPill: FC = ({ const theme = useTheme(); return ( - - - - +{resources.length} more - - + + + + + +{resources.length} more + + - - {resources.map((resource) => ( - - ))} - - + +
    + {resources.map((resource) => ( +
  • + +
  • + ))} +
+
+ + ); }; diff --git a/site/src/pages/OrganizationSettingsPage/IdpSyncPage/IdpPillList.tsx b/site/src/pages/OrganizationSettingsPage/IdpSyncPage/IdpPillList.tsx index 877ba6c9a2..330cd6bfdc 100644 --- a/site/src/pages/OrganizationSettingsPage/IdpSyncPage/IdpPillList.tsx +++ b/site/src/pages/OrganizationSettingsPage/IdpSyncPage/IdpPillList.tsx @@ -1,11 +1,12 @@ -import { type Interpolation, type Theme, useTheme } from "@emotion/react"; +import type { Interpolation, Theme } from "@emotion/react"; import Stack from "@mui/material/Stack"; -import { - Popover, - PopoverContent, - PopoverTrigger, -} from "components/deprecated/Popover/Popover"; import { Pill } from "components/Pill/Pill"; +import { + Tooltip, + TooltipContent, + TooltipProvider, + TooltipTrigger, +} from "components/Tooltip/Tooltip"; import type { FC } from "react"; import { isUUID } from "utils/uuid"; @@ -34,53 +35,26 @@ interface OverflowPillProps { } const OverflowPill: FC = ({ roles }) => { - const theme = useTheme(); - return ( - - - - +{roles.length} more - - + + + + +{roles.length} more + - - {roles.map((role) => ( - - {role} - - ))} - - + +
    + {roles.map((role) => ( +
  • + + {role} + +
  • + ))} +
+
+ + ); }; diff --git a/site/src/pages/OrganizationSettingsPage/IdpSyncPage/IdpSyncPageView.stories.tsx b/site/src/pages/OrganizationSettingsPage/IdpSyncPage/IdpSyncPageView.stories.tsx index b2eb64ab4e..71f587d43f 100644 --- a/site/src/pages/OrganizationSettingsPage/IdpSyncPage/IdpSyncPageView.stories.tsx +++ b/site/src/pages/OrganizationSettingsPage/IdpSyncPage/IdpSyncPageView.stories.tsx @@ -1,9 +1,11 @@ import { MockGroup, MockGroup2, + MockGroup3, MockGroupSyncSettings, MockGroupSyncSettings2, MockLegacyMappingGroupSyncSettings, + MockMultipleOverflowGroupSyncSettings, MockOrganization, MockRoleSyncSettings, } from "testHelpers/entities"; @@ -12,7 +14,7 @@ import { expect, userEvent } from "storybook/test"; import IdpSyncPageView from "./IdpSyncPageView"; const groupsMap = new Map(); -for (const group of [MockGroup, MockGroup2]) { +for (const group of [MockGroup, MockGroup2, MockGroup3]) { groupsMap.set(group.id, group.display_name || group.name); } @@ -70,6 +72,12 @@ export const MissingGroups: Story = { }, }; +export const MultipleOverflowGroups: Story = { + args: { + groupSyncSettings: MockMultipleOverflowGroupSyncSettings, + }, +}; + export const WithLegacyMapping: Story = { args: { groupSyncSettings: MockLegacyMappingGroupSyncSettings, diff --git a/site/src/pages/OrganizationSettingsPage/UserTable/UserRoleCell.tsx b/site/src/pages/OrganizationSettingsPage/UserTable/UserRoleCell.tsx index 0261d81e3f..2f7d378e2b 100644 --- a/site/src/pages/OrganizationSettingsPage/UserTable/UserRoleCell.tsx +++ b/site/src/pages/OrganizationSettingsPage/UserTable/UserRoleCell.tsx @@ -14,15 +14,16 @@ * users like that, though, know that it will be painful */ import { type Interpolation, type Theme, useTheme } from "@emotion/react"; -import Tooltip from "@mui/material/Tooltip"; +import MUITooltip from "@mui/material/Tooltip"; import type { LoginType, SlimRole } from "api/typesGenerated"; -import { - Popover, - PopoverContent, - PopoverTrigger, -} from "components/deprecated/Popover/Popover"; import { Pill } from "components/Pill/Pill"; import { TableCell } from "components/Table/Table"; +import { + Tooltip, + TooltipContent, + TooltipProvider, + TooltipTrigger, +} from "components/Tooltip/Tooltip"; import type { FC } from "react"; import { EditRolesButton } from "./EditRolesButton"; @@ -87,9 +88,9 @@ export const UserRoleCell: FC = ({ } > {mainDisplayRole.global ? ( - + {displayName}* - + ) : ( displayName )} @@ -109,51 +110,37 @@ const OverflowRolePill: FC = ({ roles }) => { const theme = useTheme(); return ( - - - - +{roles.length} more - - - - - {roles.map((role) => ( + + + - {role.global ? ( - - {role.display_name || role.name}* - - ) : ( - role.display_name || role.name - )} + +{roles.length} more - ))} - - + + + + {roles.map((role) => ( + + {role.global ? ( + + {role.display_name || role.name}* + + ) : ( + role.display_name || role.name + )} + + ))} + + + ); }; diff --git a/site/src/pages/UsersPage/UsersTable/UserGroupsCell.tsx b/site/src/pages/UsersPage/UsersTable/UserGroupsCell.tsx index 1cfc1db54a..909c62c26a 100644 --- a/site/src/pages/UsersPage/UsersTable/UserGroupsCell.tsx +++ b/site/src/pages/UsersPage/UsersTable/UserGroupsCell.tsx @@ -3,13 +3,14 @@ import List from "@mui/material/List"; import ListItem from "@mui/material/ListItem"; import type { Group } from "api/typesGenerated"; import { Avatar } from "components/Avatar/Avatar"; -import { - Popover, - PopoverContent, - PopoverTrigger, -} from "components/deprecated/Popover/Popover"; import { OverflowY } from "components/OverflowY/OverflowY"; import { TableCell } from "components/Table/Table"; +import { + Tooltip, + TooltipContent, + TooltipProvider, + TooltipTrigger, +} from "components/Tooltip/Tooltip"; import { UsersIcon } from "lucide-react"; import type { FC } from "react"; import { cn } from "utils/cn"; @@ -29,99 +30,83 @@ export const UserGroupsCell: FC = ({ userGroups }) => { // the table UI N/A ) : ( - - - - - - - - + + + + + + + + + {userGroups.map((group) => { + const groupName = group.display_name || group.name; + return ( + - {groupName || N/A} - - - ); - })} - - - - + + + + {groupName || N/A} + + + ); + })} + + + + + )} ); diff --git a/site/src/pages/WorkspacePage/WorkspaceNotifications/Notifications.tsx b/site/src/pages/WorkspacePage/WorkspaceNotifications/Notifications.tsx index bc72396932..308a763716 100644 --- a/site/src/pages/WorkspacePage/WorkspaceNotifications/Notifications.tsx +++ b/site/src/pages/WorkspacePage/WorkspaceNotifications/Notifications.tsx @@ -1,14 +1,14 @@ import { type Interpolation, type Theme, useTheme } from "@emotion/react"; import type { AlertProps } from "components/Alert/Alert"; import { Button, type ButtonProps } from "components/Button/Button"; -import { - Popover, - PopoverContent, - PopoverTrigger, - usePopover, -} from "components/deprecated/Popover/Popover"; import { Pill } from "components/Pill/Pill"; -import type { FC, ReactNode } from "react"; +import { + Tooltip, + TooltipContent, + TooltipProvider, + TooltipTrigger, +} from "components/Tooltip/Tooltip"; +import { type FC, type ReactNode, useState } from "react"; import type { ThemeRole } from "theme/roles"; export type NotificationItem = { @@ -29,48 +29,58 @@ export const Notifications: FC = ({ severity, icon, }) => { + const [isOpen, setIsOpen] = useState(false); const theme = useTheme(); return ( - - -
- -
-
- + + +
+ +
+
+ - {items.map((n) => ( - - ))} -
-
+ }} + > + {items.map((n) => ( + + ))} + + + ); }; -const NotificationPill: FC = ({ +type NotificationPillProps = NotificationsProps & { + isTooltipOpen: boolean; +}; + +const NotificationPill: FC = ({ items, severity, icon, + isTooltipOpen, }) => { - const popover = usePopover(); - return ( ({ "& svg": { color: theme.roles[severity].outline }, - borderColor: popover.open ? theme.roles[severity].outline : undefined, + borderColor: isTooltipOpen ? theme.roles[severity].outline : undefined, })} > {items.length} @@ -99,7 +109,7 @@ export const NotificationActionButton: FC = (props) => { }; const styles = { - // Adds some spacing from the popover content + // Adds some spacing from the Tooltip content pillContainer: { padding: "8px 0", }, diff --git a/site/src/pages/WorkspacePage/WorkspaceNotifications/WorkspaceNotifications.stories.tsx b/site/src/pages/WorkspacePage/WorkspaceNotifications/WorkspaceNotifications.stories.tsx index bcff8c53cc..fc68015746 100644 --- a/site/src/pages/WorkspacePage/WorkspaceNotifications/WorkspaceNotifications.stories.tsx +++ b/site/src/pages/WorkspacePage/WorkspaceNotifications/WorkspaceNotifications.stories.tsx @@ -9,7 +9,7 @@ import { withDashboardProvider } from "testHelpers/storybook"; import type { Meta, StoryObj } from "@storybook/react-vite"; import { getWorkspaceResolveAutostartQueryKey } from "api/queries/workspaceQuota"; import type { WorkspacePermissions } from "modules/workspaces/permissions"; -import { expect, userEvent, waitFor, within } from "storybook/test"; +import { expect, screen, userEvent, waitFor } from "storybook/test"; import { WorkspaceNotifications } from "./WorkspaceNotifications"; export const defaultPermissions: WorkspacePermissions = { @@ -51,15 +51,13 @@ export const Outdated: Story = { workspace: MockOutdatedWorkspace, }, - play: async ({ canvasElement, step }) => { - const screen = within(canvasElement); - + play: async ({ step }) => { await step("activate hover trigger", async () => { await userEvent.hover(screen.getByTestId("info-notifications")); await waitFor(() => - expect( - screen.getByText(MockTemplateVersion.message), - ).toBeInTheDocument(), + expect(screen.getByRole("tooltip")).toHaveTextContent( + MockTemplateVersion.message, + ), ); }); }, @@ -71,13 +69,13 @@ export const OutdatedWithMarkdownMessage: Story = { latestVersion: MockTemplateVersionWithMarkdownMessage, }, - play: async ({ canvasElement, step }) => { - const screen = within(canvasElement); - + play: async ({ step }) => { await step("activate hover trigger", async () => { await userEvent.hover(screen.getByTestId("info-notifications")); await waitFor(() => - expect(screen.getByText(/an update is available/i)).toBeInTheDocument(), + expect(screen.getByRole("tooltip")).toHaveTextContent( + /an update is available/i, + ), ); }); }, @@ -102,15 +100,13 @@ export const RequiresManualUpdate: Story = { ], }, - play: async ({ canvasElement, step }) => { - const screen = within(canvasElement); - + play: async ({ step }) => { await step("activate hover trigger", async () => { await userEvent.hover(screen.getByTestId("warning-notifications")); await waitFor(() => - expect( - screen.getByText(/unable to automatically update/i), - ).toBeInTheDocument(), + expect(screen.getByRole("tooltip")).toHaveTextContent( + /unable to automatically update/i, + ), ); }); }, @@ -131,13 +127,13 @@ export const Unhealthy: Story = { }, }, - play: async ({ canvasElement, step }) => { - const screen = within(canvasElement); - + play: async ({ step }) => { await step("activate hover trigger", async () => { await userEvent.hover(screen.getByTestId("warning-notifications")); await waitFor(() => - expect(screen.getByText(/workspace is unhealthy/i)).toBeInTheDocument(), + expect(screen.getByRole("tooltip")).toHaveTextContent( + /workspace is unhealthy/i, + ), ); }); }, @@ -165,13 +161,13 @@ export const Dormant: Story = { workspace: DormantWorkspace, }, - play: async ({ canvasElement, step }) => { - const screen = within(canvasElement); - + play: async ({ step }) => { await step("activate hover trigger", async () => { await userEvent.hover(screen.getByTestId("warning-notifications")); await waitFor(() => - expect(screen.getByText(/workspace is dormant/i)).toBeInTheDocument(), + expect(screen.getByRole("tooltip")).toHaveTextContent( + /workspace is dormant/i, + ), ); }); }, @@ -205,13 +201,13 @@ export const PendingInQueue: Story = { }, }, - play: async ({ canvasElement, step }) => { - const screen = within(canvasElement); - + play: async ({ step }) => { await step("activate hover trigger", async () => { await userEvent.hover(await screen.findByTestId("info-notifications")); await waitFor(() => - expect(screen.getByText(/build is pending/i)).toBeInTheDocument(), + expect(screen.getByRole("tooltip")).toHaveTextContent( + /build is pending/i, + ), ); }); }, @@ -227,13 +223,13 @@ export const TemplateDeprecated: Story = { }, }, - play: async ({ canvasElement, step }) => { - const screen = within(canvasElement); - + play: async ({ step }) => { await step("activate hover trigger", async () => { await userEvent.hover(screen.getByTestId("warning-notifications")); await waitFor(() => - expect(screen.getByText(/deprecated template/i)).toBeInTheDocument(), + expect(screen.getByRole("tooltip")).toHaveTextContent( + /deprecated template/i, + ), ); }); }, diff --git a/site/src/testHelpers/entities.ts b/site/src/testHelpers/entities.ts index 3e353d7e77..3925538c4c 100644 --- a/site/src/testHelpers/entities.ts +++ b/site/src/testHelpers/entities.ts @@ -39,6 +39,18 @@ export const MockOrganization2: TypesGen.Organization = { is_default: false, }; +export const MockOrganization3: TypesGen.Organization = { + id: "my-organization-3-id", + name: "my-organization-3", + display_name: "My Organization 3", + description: + "Yet another organization that will show up in OrganizationPills.", + icon: "/emojis/1f957.png", + created_at: "", + updated_at: "", + is_default: false, +}; + export const MockTemplateDAUResponse: TypesGen.DAUsResponse = { tz_hour_offset: 0, entries: [ @@ -2905,6 +2917,20 @@ export const MockGroupSyncSettings2: TypesGen.GroupSyncSettings = { auto_create_missing_groups: false, }; +export const MockMultipleOverflowGroupSyncSettings: TypesGen.GroupSyncSettings = + { + field: "group-multiple-overflow-test", + mapping: { + "idp-group-1": [ + "fbd2116a-8961-4954-87ae-e4575bd29ce0", + "13de3eb4-9b4f-49e7-b0f8-0c3728a0d2e2", + "d3562dc1-c120-43a9-ba02-88e43bbca192", + ], + }, + regex_filter: "@[a-zA-Z0-9_]+", + auto_create_missing_groups: false, + }; + export const MockRoleSyncSettings: TypesGen.RoleSyncSettings = { field: "role-test", mapping: { @@ -2929,7 +2955,11 @@ export const MockOrganizationSyncSettings2: TypesGen.OrganizationSyncSettings = { field: "organization-test", mapping: { - "idp-org-1": ["my-organization-id", "my-organization-2-id"], + "idp-org-1": [ + "my-organization-id", + "my-organization-2-id", + "my-organization-3-id", + ], "idp-org-2": ["my-organization-id"], }, organization_assign_default: true, @@ -2970,6 +3000,20 @@ export const MockGroup2: TypesGen.Group = { total_member_count: 2, }; +export const MockGroup3: TypesGen.Group = { + id: "d3562dc1-c120-43a9-ba02-88e43bbca192", + name: "Back-End", + display_name: "", + avatar_url: "https://example.com", + organization_id: MockOrganization.id, + organization_name: MockOrganization.name, + organization_display_name: MockOrganization.display_name, + members: [MockUserOwner, MockUserMember], + quota_allowance: 5, + source: "user", + total_member_count: 2, +}; + const MockEveryoneGroup: TypesGen.Group = { // The "Everyone" group must have the same ID as a the organization it belongs // to.