From 13ee34e022e546cfa7492c572aab84e89bccf07b Mon Sep 17 00:00:00 2001 From: "blink-so[bot]" <211532188+blink-so[bot]@users.noreply.github.com> Date: Tue, 17 Jun 2025 16:04:49 +0000 Subject: [PATCH 1/4] feat: migrate Alert component from MUI to shadcn - Replace MUI Alert with shadcn-based implementation using class-variance-authority - Maintain backward compatibility with existing Alert API (severity, dismissible, actions, onDismiss) - Update AlertTitle imports across codebase to use new implementation - Add proper Tailwind CSS styling with dark mode support - Preserve accessibility features and smooth animations - Support all existing severity levels: info, success, warning, error Co-authored-by: jaaydenh <1858163+jaaydenh@users.noreply.github.com> --- site/src/components/Alert/Alert.tsx | 107 +++++++++++++----- site/src/components/Alert/ErrorAlert.tsx | 3 +- .../GitDeviceAuth/GitDeviceAuth.tsx | 4 +- .../modules/provisioners/ProvisionerAlert.tsx | 4 +- .../ChangeWorkspaceVersionDialog.tsx | 4 +- .../OverviewPage/OverviewPageView.tsx | 4 +- site/src/pages/SetupPage/SetupPageView.tsx | 4 +- site/src/pages/WorkspacePage/Workspace.tsx | 4 +- 8 files changed, 92 insertions(+), 42 deletions(-) diff --git a/site/src/components/Alert/Alert.tsx b/site/src/components/Alert/Alert.tsx index e97b690f82833..fae243f86ac03 100644 --- a/site/src/components/Alert/Alert.tsx +++ b/site/src/components/Alert/Alert.tsx @@ -1,24 +1,45 @@ -import MuiAlert, { - type AlertColor as MuiAlertColor, - type AlertProps as MuiAlertProps, - // biome-ignore lint/nursery/noRestrictedImports: Used as base component -} from "@mui/material/Alert"; +import { cva, type VariantProps } from "class-variance-authority"; import Collapse from "@mui/material/Collapse"; import { Button } from "components/Button/Button"; -import { - type FC, - type PropsWithChildren, - type ReactNode, - useState, -} from "react"; +import { forwardRef, useState, type FC, type PropsWithChildren, type ReactNode } from "react"; +import { cn } from "utils/cn"; -export type AlertColor = MuiAlertColor; +const alertVariants = cva( + "relative w-full rounded-lg border p-4 text-left", + { + variants: { + variant: { + default: "bg-surface-primary text-content-primary border-border-default", + info: "bg-blue-50 text-blue-900 border-blue-200 dark:bg-blue-950 dark:text-blue-100 dark:border-blue-800", + success: "bg-green-50 text-green-900 border-green-200 dark:bg-green-950 dark:text-green-100 dark:border-green-800", + warning: "bg-yellow-50 text-yellow-900 border-yellow-200 dark:bg-yellow-950 dark:text-yellow-100 dark:border-yellow-800", + error: "bg-red-50 text-red-900 border-red-200 dark:bg-red-950 dark:text-red-100 dark:border-red-800", + }, + }, + defaultVariants: { + variant: "default", + }, + }, +); -export type AlertProps = MuiAlertProps & { +// Map MUI severity to our variant +const severityToVariant = { + info: "info", + success: "success", + warning: "warning", + error: "error", +} as const; + +export type AlertColor = "info" | "success" | "warning" | "error"; + +export type AlertProps = { actions?: ReactNode; dismissible?: boolean; onDismiss?: () => void; -}; + severity?: AlertColor; + children?: ReactNode; + className?: string; +} & VariantProps; export const Alert: FC = ({ children, @@ -26,7 +47,9 @@ export const Alert: FC = ({ dismissible, severity = "info", onDismiss, - ...alertProps + className, + variant, + ...props }) => { const [open, setOpen] = useState(true); @@ -37,14 +60,21 @@ export const Alert: FC = ({ return null; } + // Use severity to determine variant if variant is not explicitly provided + const finalVariant = variant || (severity in severityToVariant ? severityToVariant[severity] : "default"); + return ( - +
+
+
+ {children} +
+
{/* CTAs passed in by the consumer */} {actions} @@ -62,11 +92,9 @@ export const Alert: FC = ({ Dismiss )} - - } - > - {children} - +
+
+
); }; @@ -74,10 +102,35 @@ export const Alert: FC = ({ export const AlertDetail: FC = ({ children }) => { return ( ({ color: theme.palette.text.secondary, fontSize: 13 })} + className="text-sm opacity-75" data-chromatic="ignore" > {children} ); }; + +// Export AlertTitle and AlertDescription for compatibility +export const AlertTitle = forwardRef< + HTMLHeadingElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)); +AlertTitle.displayName = "AlertTitle"; + +export const AlertDescription = forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)); +AlertDescription.displayName = "AlertDescription"; \ No newline at end of file diff --git a/site/src/components/Alert/ErrorAlert.tsx b/site/src/components/Alert/ErrorAlert.tsx index 0198ea4e99540..d1bc4c0a9dd9e 100644 --- a/site/src/components/Alert/ErrorAlert.tsx +++ b/site/src/components/Alert/ErrorAlert.tsx @@ -1,8 +1,7 @@ -import AlertTitle from "@mui/material/AlertTitle"; import { getErrorDetail, getErrorMessage, getErrorStatus } from "api/errors"; import type { FC } from "react"; import { Link } from "../Link/Link"; -import { Alert, AlertDetail, type AlertProps } from "./Alert"; +import { Alert, AlertDetail, AlertTitle, type AlertProps } from "./Alert"; export const ErrorAlert: FC< Omit & { error: unknown } diff --git a/site/src/components/GitDeviceAuth/GitDeviceAuth.tsx b/site/src/components/GitDeviceAuth/GitDeviceAuth.tsx index 7b3d8091abfeb..e8e74c1fe42a0 100644 --- a/site/src/components/GitDeviceAuth/GitDeviceAuth.tsx +++ b/site/src/components/GitDeviceAuth/GitDeviceAuth.tsx @@ -1,11 +1,11 @@ import type { Interpolation, Theme } from "@emotion/react"; -import AlertTitle from "@mui/material/AlertTitle"; + import CircularProgress from "@mui/material/CircularProgress"; import Link from "@mui/material/Link"; import type { ApiErrorResponse } from "api/errors"; import type { ExternalAuthDevice } from "api/typesGenerated"; import { isAxiosError } from "axios"; -import { Alert, AlertDetail } from "components/Alert/Alert"; +import { Alert, AlertDetail, AlertTitle } from "components/Alert/Alert"; import { CopyButton } from "components/CopyButton/CopyButton"; import { ExternalLinkIcon } from "lucide-react"; import type { FC } from "react"; diff --git a/site/src/modules/provisioners/ProvisionerAlert.tsx b/site/src/modules/provisioners/ProvisionerAlert.tsx index 2d14237b414ed..ee02df3cdb25d 100644 --- a/site/src/modules/provisioners/ProvisionerAlert.tsx +++ b/site/src/modules/provisioners/ProvisionerAlert.tsx @@ -1,7 +1,5 @@ import type { Theme } from "@emotion/react"; -import AlertTitle from "@mui/material/AlertTitle"; -import { Alert, type AlertColor } from "components/Alert/Alert"; -import { AlertDetail } from "components/Alert/Alert"; +import { Alert, AlertDetail, AlertTitle, type AlertColor } from "components/Alert/Alert"; import { ProvisionerTag } from "modules/provisioners/ProvisionerTag"; import type { FC } from "react"; diff --git a/site/src/modules/workspaces/WorkspaceMoreActions/ChangeWorkspaceVersionDialog.tsx b/site/src/modules/workspaces/WorkspaceMoreActions/ChangeWorkspaceVersionDialog.tsx index ecedc5aef6b5f..3f06933dc04d3 100644 --- a/site/src/modules/workspaces/WorkspaceMoreActions/ChangeWorkspaceVersionDialog.tsx +++ b/site/src/modules/workspaces/WorkspaceMoreActions/ChangeWorkspaceVersionDialog.tsx @@ -1,11 +1,11 @@ import { css } from "@emotion/css"; -import AlertTitle from "@mui/material/AlertTitle"; + import Autocomplete from "@mui/material/Autocomplete"; import CircularProgress from "@mui/material/CircularProgress"; import TextField from "@mui/material/TextField"; import { templateVersions } from "api/queries/templates"; import type { TemplateVersion, Workspace } from "api/typesGenerated"; -import { Alert } from "components/Alert/Alert"; +import { Alert, AlertTitle } from "components/Alert/Alert"; import { Avatar } from "components/Avatar/Avatar"; import { AvatarData } from "components/Avatar/AvatarData"; import { ConfirmDialog } from "components/Dialogs/ConfirmDialog/ConfirmDialog"; diff --git a/site/src/pages/DeploymentSettingsPage/OverviewPage/OverviewPageView.tsx b/site/src/pages/DeploymentSettingsPage/OverviewPage/OverviewPageView.tsx index 505036a9c821f..7f487edef1675 100644 --- a/site/src/pages/DeploymentSettingsPage/OverviewPage/OverviewPageView.tsx +++ b/site/src/pages/DeploymentSettingsPage/OverviewPage/OverviewPageView.tsx @@ -1,4 +1,4 @@ -import AlertTitle from "@mui/material/AlertTitle"; + import type { DAUsResponse, Experiments, @@ -15,7 +15,7 @@ import { Stack } from "components/Stack/Stack"; import type { FC } from "react"; import { useDeploymentOptions } from "utils/deployOptions"; import { docs } from "utils/docs"; -import { Alert } from "../../../components/Alert/Alert"; +import { Alert, AlertTitle } from "../../../components/Alert/Alert"; import OptionsTable from "../OptionsTable"; import { UserEngagementChart } from "./UserEngagementChart"; diff --git a/site/src/pages/SetupPage/SetupPageView.tsx b/site/src/pages/SetupPage/SetupPageView.tsx index 96654bdda8514..a27ebebd5f696 100644 --- a/site/src/pages/SetupPage/SetupPageView.tsx +++ b/site/src/pages/SetupPage/SetupPageView.tsx @@ -1,5 +1,5 @@ import GitHubIcon from "@mui/icons-material/GitHub"; -import AlertTitle from "@mui/material/AlertTitle"; + import Autocomplete from "@mui/material/Autocomplete"; import MuiButton from "@mui/material/Button"; import Checkbox from "@mui/material/Checkbox"; @@ -9,7 +9,7 @@ import TextField from "@mui/material/TextField"; import { countries } from "api/countriesGenerated"; import type * as TypesGen from "api/typesGenerated"; import { isAxiosError } from "axios"; -import { Alert, AlertDetail } from "components/Alert/Alert"; +import { Alert, AlertDetail, AlertTitle } from "components/Alert/Alert"; import { Button } from "components/Button/Button"; import { FormFields, VerticalForm } from "components/Form/Form"; import { CoderIcon } from "components/Icons/CoderIcon"; diff --git a/site/src/pages/WorkspacePage/Workspace.tsx b/site/src/pages/WorkspacePage/Workspace.tsx index 5c032c04efbdf..22053bca1b121 100644 --- a/site/src/pages/WorkspacePage/Workspace.tsx +++ b/site/src/pages/WorkspacePage/Workspace.tsx @@ -2,9 +2,9 @@ import type { Interpolation, Theme } from "@emotion/react"; import { useTheme } from "@emotion/react"; import HistoryOutlined from "@mui/icons-material/HistoryOutlined"; import HubOutlined from "@mui/icons-material/HubOutlined"; -import AlertTitle from "@mui/material/AlertTitle"; + import type * as TypesGen from "api/typesGenerated"; -import { Alert, AlertDetail } from "components/Alert/Alert"; +import { Alert, AlertDetail, AlertTitle } from "components/Alert/Alert"; import { SidebarIconButton } from "components/FullPageLayout/Sidebar"; import { useSearchParamsKey } from "hooks/useSearchParamsKey"; import { ProvisionerStatusAlert } from "modules/provisioners/ProvisionerStatusAlert"; From 29ab8c404a8c85aefb772a6b5ff8d207cd9bdc21 Mon Sep 17 00:00:00 2001 From: "blink-so[bot]" <211532188+blink-so[bot]@users.noreply.github.com> Date: Wed, 18 Jun 2025 16:44:22 +0000 Subject: [PATCH 2/4] fix: remove unused AlertDescription export to fix lint error Co-authored-by: jaaydenh <1858163+jaaydenh@users.noreply.github.com> --- site/src/components/Alert/Alert.tsx | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/site/src/components/Alert/Alert.tsx b/site/src/components/Alert/Alert.tsx index fae243f86ac03..b336eb80c148a 100644 --- a/site/src/components/Alert/Alert.tsx +++ b/site/src/components/Alert/Alert.tsx @@ -123,14 +123,3 @@ export const AlertTitle = forwardRef< )); AlertTitle.displayName = "AlertTitle"; -export const AlertDescription = forwardRef< - HTMLDivElement, - React.HTMLAttributes ->(({ className, ...props }, ref) => ( -
-)); -AlertDescription.displayName = "AlertDescription"; \ No newline at end of file From 4fe653d5968439faeb2b1a25312023514a86afc6 Mon Sep 17 00:00:00 2001 From: Jaayden Halko Date: Wed, 18 Jun 2025 21:41:11 +0000 Subject: [PATCH 3/4] fix: cleanup --- site/src/components/Alert/Alert.tsx | 92 +++++++++---------- site/src/components/Alert/ErrorAlert.tsx | 2 +- .../modules/provisioners/ProvisionerAlert.tsx | 7 +- .../OverviewPage/OverviewPageView.tsx | 1 - 4 files changed, 51 insertions(+), 51 deletions(-) diff --git a/site/src/components/Alert/Alert.tsx b/site/src/components/Alert/Alert.tsx index b336eb80c148a..0848e1e574031 100644 --- a/site/src/components/Alert/Alert.tsx +++ b/site/src/components/Alert/Alert.tsx @@ -1,19 +1,24 @@ -import { cva, type VariantProps } from "class-variance-authority"; -import Collapse from "@mui/material/Collapse"; +import { type VariantProps, cva } from "class-variance-authority"; import { Button } from "components/Button/Button"; -import { forwardRef, useState, type FC, type PropsWithChildren, type ReactNode } from "react"; +import { + type FC, + type PropsWithChildren, + type ReactNode, + forwardRef, + useState, +} from "react"; import { cn } from "utils/cn"; const alertVariants = cva( - "relative w-full rounded-lg border p-4 text-left", + "relative w-full rounded-lg border border-solid p-4 text-left", { variants: { variant: { - default: "bg-surface-primary text-content-primary border-border-default", - info: "bg-blue-50 text-blue-900 border-blue-200 dark:bg-blue-950 dark:text-blue-100 dark:border-blue-800", - success: "bg-green-50 text-green-900 border-green-200 dark:bg-green-950 dark:text-green-100 dark:border-green-800", - warning: "bg-yellow-50 text-yellow-900 border-yellow-200 dark:bg-yellow-950 dark:text-yellow-100 dark:border-yellow-800", - error: "bg-red-50 text-red-900 border-red-200 dark:bg-red-950 dark:text-red-100 dark:border-red-800", + default: "border-border-default", + info: "border-surface-sky", + success: "border-surface-green", + warning: "border-border-warning", + error: "border-border-destructive", }, }, defaultVariants: { @@ -25,7 +30,7 @@ const alertVariants = cva( // Map MUI severity to our variant const severityToVariant = { info: "info", - success: "success", + success: "success", warning: "warning", error: "error", } as const; @@ -53,58 +58,49 @@ export const Alert: FC = ({ }) => { const [open, setOpen] = useState(true); - // Can't only rely on MUI's hiding behavior inside flex layouts, because even - // though MUI will make a dismissed alert have zero height, the alert will - // still behave as a flex child and introduce extra row/column gaps if (!open) { return null; } // Use severity to determine variant if variant is not explicitly provided - const finalVariant = variant || (severity in severityToVariant ? severityToVariant[severity] : "default"); + const finalVariant = + variant || + (severity in severityToVariant ? severityToVariant[severity] : "default"); return ( - -
-
-
- {children} -
-
- {/* CTAs passed in by the consumer */} - {actions} +
+
+
{children}
+
+ {/* CTAs passed in by the consumer */} + {actions} - {/* close CTA */} - {dismissible && ( - - )} -
+ {dismissible && ( + + )}
- +
); }; export const AlertDetail: FC = ({ children }) => { return ( - + {children} ); @@ -121,5 +117,5 @@ export const AlertTitle = forwardRef< {...props} /> )); -AlertTitle.displayName = "AlertTitle"; +AlertTitle.displayName = "AlertTitle"; diff --git a/site/src/components/Alert/ErrorAlert.tsx b/site/src/components/Alert/ErrorAlert.tsx index d1bc4c0a9dd9e..210a3f032da9f 100644 --- a/site/src/components/Alert/ErrorAlert.tsx +++ b/site/src/components/Alert/ErrorAlert.tsx @@ -1,7 +1,7 @@ import { getErrorDetail, getErrorMessage, getErrorStatus } from "api/errors"; import type { FC } from "react"; import { Link } from "../Link/Link"; -import { Alert, AlertDetail, AlertTitle, type AlertProps } from "./Alert"; +import { Alert, AlertDetail, type AlertProps, AlertTitle } from "./Alert"; export const ErrorAlert: FC< Omit & { error: unknown } diff --git a/site/src/modules/provisioners/ProvisionerAlert.tsx b/site/src/modules/provisioners/ProvisionerAlert.tsx index ee02df3cdb25d..eebe4ea0bbd8c 100644 --- a/site/src/modules/provisioners/ProvisionerAlert.tsx +++ b/site/src/modules/provisioners/ProvisionerAlert.tsx @@ -1,5 +1,10 @@ import type { Theme } from "@emotion/react"; -import { Alert, AlertDetail, AlertTitle, type AlertColor } from "components/Alert/Alert"; +import { + Alert, + type AlertColor, + AlertDetail, + AlertTitle, +} from "components/Alert/Alert"; import { ProvisionerTag } from "modules/provisioners/ProvisionerTag"; import type { FC } from "react"; diff --git a/site/src/pages/DeploymentSettingsPage/OverviewPage/OverviewPageView.tsx b/site/src/pages/DeploymentSettingsPage/OverviewPage/OverviewPageView.tsx index 7f487edef1675..a5e2f65ca34ea 100644 --- a/site/src/pages/DeploymentSettingsPage/OverviewPage/OverviewPageView.tsx +++ b/site/src/pages/DeploymentSettingsPage/OverviewPage/OverviewPageView.tsx @@ -1,4 +1,3 @@ - import type { DAUsResponse, Experiments, From 341b86fbfb66ca2ee27ef98e87eb512213852482 Mon Sep 17 00:00:00 2001 From: Jaayden Halko Date: Fri, 20 Jun 2025 12:37:39 +0000 Subject: [PATCH 4/4] fix: update color --- site/src/components/Alert/Alert.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site/src/components/Alert/Alert.tsx b/site/src/components/Alert/Alert.tsx index 0848e1e574031..772801132fee9 100644 --- a/site/src/components/Alert/Alert.tsx +++ b/site/src/components/Alert/Alert.tsx @@ -15,7 +15,7 @@ const alertVariants = cva( variants: { variant: { default: "border-border-default", - info: "border-surface-sky", + info: "border-highlight-sky", success: "border-surface-green", warning: "border-border-warning", error: "border-border-destructive", pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy