mirror of
https://github.com/formbricks/formbricks.git
synced 2026-01-06 09:00:18 -06:00
fix: unify alert component (#5002)
Co-authored-by: Dhruwang <dhruwangjariwala18@gmail.com>
This commit is contained in:
74
apps/web/modules/ui/components/alert/index.test.tsx
Normal file
74
apps/web/modules/ui/components/alert/index.test.tsx
Normal file
@@ -0,0 +1,74 @@
|
||||
import { fireEvent, render, screen } from "@testing-library/react";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { Alert, AlertButton, AlertDescription, AlertTitle } from "./index";
|
||||
|
||||
describe("Alert", () => {
|
||||
it("renders with default variant", () => {
|
||||
render(
|
||||
<Alert>
|
||||
<AlertTitle>Test Title</AlertTitle>
|
||||
<AlertDescription>Test Description</AlertDescription>
|
||||
</Alert>
|
||||
);
|
||||
|
||||
expect(screen.getByRole("alert")).toBeInTheDocument();
|
||||
expect(screen.getByText("Test Title")).toBeInTheDocument();
|
||||
expect(screen.getByText("Test Description")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("renders with different variants", () => {
|
||||
const variants = ["default", "error", "warning", "info", "success"] as const;
|
||||
|
||||
variants.forEach((variant) => {
|
||||
const { container } = render(
|
||||
<Alert variant={variant}>
|
||||
<AlertTitle>Test Title</AlertTitle>
|
||||
</Alert>
|
||||
);
|
||||
|
||||
expect(container.firstChild).toHaveClass(
|
||||
variant === "default" ? "text-foreground" : `text-${variant}-foreground`
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it("renders with different sizes", () => {
|
||||
const sizes = ["default", "small"] as const;
|
||||
|
||||
sizes.forEach((size) => {
|
||||
const { container } = render(
|
||||
<Alert size={size}>
|
||||
<AlertTitle>Test Title</AlertTitle>
|
||||
</Alert>
|
||||
);
|
||||
|
||||
expect(container.firstChild).toHaveClass(size === "default" ? "py-3" : "py-2");
|
||||
});
|
||||
});
|
||||
|
||||
it("renders with button and handles click", () => {
|
||||
const handleClick = vi.fn();
|
||||
|
||||
render(
|
||||
<Alert>
|
||||
<AlertTitle>Test Title</AlertTitle>
|
||||
<AlertButton onClick={handleClick}>Click me</AlertButton>
|
||||
</Alert>
|
||||
);
|
||||
|
||||
const button = screen.getByText("Click me");
|
||||
expect(button).toBeInTheDocument();
|
||||
fireEvent.click(button);
|
||||
expect(handleClick).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it("applies custom className", () => {
|
||||
const { container } = render(
|
||||
<Alert className="custom-class">
|
||||
<AlertTitle>Test Title</AlertTitle>
|
||||
</Alert>
|
||||
);
|
||||
|
||||
expect(container.firstChild).toHaveClass("custom-class");
|
||||
});
|
||||
});
|
||||
@@ -1,49 +1,141 @@
|
||||
import { VariantProps, cva } from "class-variance-authority";
|
||||
import * as React from "react";
|
||||
import { cn } from "@formbricks/lib/cn";
|
||||
"use client";
|
||||
|
||||
const alertVariants = cva(
|
||||
"relative w-full rounded-xl border p-3 [&>svg]:absolute [&>svg]:text-foreground [&>svg]:left-3 [&>svg]:top-3 [&>svg+div]:translate-y-[-3px] [&:has(svg)]:pl-9",
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
default: "bg-background text-foreground",
|
||||
destructive:
|
||||
"text-destructive border-destructive/50 dark:border-destructive [&>svg]:text-destructive text-destructive",
|
||||
info: "text-slate-800 bg-brand/5",
|
||||
warning: "text-yellow-700 bg-yellow-50",
|
||||
error: "border-error/50 dark:border-error [&>svg]:text-error text-error",
|
||||
},
|
||||
import { VariantProps, cva } from "class-variance-authority";
|
||||
import { AlertCircle, AlertTriangle, CheckCircle2Icon, Info } from "lucide-react";
|
||||
import * as React from "react";
|
||||
import { createContext, useContext } from "react";
|
||||
import { cn } from "@formbricks/lib/cn";
|
||||
import { Button, ButtonProps } from "../button";
|
||||
|
||||
// Create a context to share variant and size with child components
|
||||
interface AlertContextValue {
|
||||
variant?: "default" | "error" | "warning" | "info" | "success" | null;
|
||||
size?: "default" | "small" | null;
|
||||
}
|
||||
|
||||
const AlertContext = createContext<AlertContextValue>({
|
||||
variant: "default",
|
||||
size: "default",
|
||||
});
|
||||
|
||||
const useAlertContext = () => useContext(AlertContext);
|
||||
|
||||
// Define alert styles with variants
|
||||
const alertVariants = cva("relative w-full rounded-lg border [&>svg]:size-4 [&>svg]:text-foreground", {
|
||||
variants: {
|
||||
variant: {
|
||||
default: "text-foreground border-border",
|
||||
error:
|
||||
"text-error-foreground border-error/50 [&_button]:bg-error-background [&_button]:text-error-foreground [&_button:hover]:bg-error-background-muted",
|
||||
warning:
|
||||
"text-warning-foreground border-warning/50 [&_button]:bg-warning-background [&_button]:text-warning-foreground [&_button:hover]:bg-warning-background-muted",
|
||||
info: "text-info-foreground border-info/50 [&_button]:bg-info-background [&_button]:text-info-foreground [&_button:hover]:bg-info-background-muted",
|
||||
success:
|
||||
"text-success-foreground border-success/50 [&_button]:bg-success-background [&_button]:text-success-foreground [&_button:hover]:bg-success-background-muted",
|
||||
},
|
||||
defaultVariants: {
|
||||
variant: "default",
|
||||
size: {
|
||||
default:
|
||||
"py-3 px-4 text-sm grid grid-cols-[1fr_auto] grid-rows-[auto_auto] gap-x-3 [&>svg]:absolute [&>svg]:left-4 [&>svg]:top-4 [&>svg~*]:pl-7",
|
||||
small:
|
||||
"px-3 py-2 text-xs flex items-center justify-between gap-2 [&>svg]:flex-shrink-0 [&_button]:text-xs [&_button]:bg-transparent [&_button:hover]:bg-transparent [&>svg~*]:pl-0",
|
||||
},
|
||||
}
|
||||
);
|
||||
},
|
||||
defaultVariants: {
|
||||
variant: "default",
|
||||
size: "default",
|
||||
},
|
||||
});
|
||||
|
||||
const alertVariantIcons: Record<"default" | "error" | "warning" | "info" | "success", React.ReactNode> = {
|
||||
default: null,
|
||||
error: <AlertCircle className="size-4" />,
|
||||
warning: <AlertTriangle className="size-4" />,
|
||||
info: <Info className="size-4" />,
|
||||
success: <CheckCircle2Icon className="size-4" />,
|
||||
};
|
||||
|
||||
const Alert = React.forwardRef<
|
||||
HTMLDivElement,
|
||||
React.HTMLAttributes<HTMLDivElement> &
|
||||
VariantProps<typeof alertVariants> & { dangerouslySetInnerHTML?: { __html: string } }
|
||||
>(({ className, variant, ...props }, ref) => (
|
||||
<div ref={ref} role="alert" className={cn(alertVariants({ variant }), className)} {...props} />
|
||||
));
|
||||
React.HTMLAttributes<HTMLDivElement> & VariantProps<typeof alertVariants>
|
||||
>(({ className, variant, size, ...props }, ref) => {
|
||||
const variantIcon = variant ? (variant !== "default" ? alertVariantIcons[variant] : null) : null;
|
||||
|
||||
return (
|
||||
<AlertContext.Provider value={{ variant, size }}>
|
||||
<div ref={ref} role="alert" className={cn(alertVariants({ variant, size }), className)} {...props}>
|
||||
{variantIcon}
|
||||
{props.children}
|
||||
</div>
|
||||
</AlertContext.Provider>
|
||||
);
|
||||
});
|
||||
Alert.displayName = "Alert";
|
||||
|
||||
const AlertTitle = React.forwardRef<
|
||||
HTMLParagraphElement,
|
||||
React.HTMLAttributes<HTMLHeadingElement> & { dangerouslySetInnerHTML?: { __html: string } }
|
||||
>(({ className, ...props }, ref) => (
|
||||
<h5 ref={ref} className={cn("mb-1 cursor-default font-medium leading-none", className)} {...props} />
|
||||
));
|
||||
const AlertTitle = React.forwardRef<HTMLParagraphElement, React.HTMLAttributes<HTMLHeadingElement>>(
|
||||
({ className, ...props }, ref) => {
|
||||
const { size } = useAlertContext();
|
||||
return (
|
||||
<h5
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"col-start-1 row-start-1 font-medium leading-none tracking-tight",
|
||||
size === "small" ? "min-w-0 flex-shrink truncate" : "col-start-1 row-start-1",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
AlertTitle.displayName = "AlertTitle";
|
||||
|
||||
const AlertDescription = React.forwardRef<
|
||||
HTMLParagraphElement,
|
||||
React.HTMLAttributes<HTMLParagraphElement> & { dangerouslySetInnerHTML?: { __html: string } }
|
||||
>(({ className, ...props }, ref) => (
|
||||
<div ref={ref} className={cn("cursor-default text-sm [&_p]:leading-relaxed", className)} {...props} />
|
||||
));
|
||||
const AlertDescription = React.forwardRef<HTMLParagraphElement, React.HTMLAttributes<HTMLParagraphElement>>(
|
||||
({ className, ...props }, ref) => {
|
||||
const { size } = useAlertContext();
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"[&_p]:leading-relaxed",
|
||||
size === "small"
|
||||
? "hidden min-w-0 flex-shrink flex-grow truncate opacity-80 sm:block" // Hidden on very small screens, limited width
|
||||
: "col-start-1 row-start-2",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
);
|
||||
AlertDescription.displayName = "AlertDescription";
|
||||
|
||||
export { Alert, AlertDescription, AlertTitle };
|
||||
const AlertButton = React.forwardRef<HTMLButtonElement, ButtonProps>(
|
||||
({ className, variant, size, children, ...props }, ref) => {
|
||||
const { size: alertSize } = useAlertContext();
|
||||
|
||||
// Determine button styling based on alert context
|
||||
const buttonVariant = variant || (alertSize === "small" ? "link" : "secondary");
|
||||
const buttonSize = size || (alertSize === "small" ? "sm" : "default");
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
"self-end",
|
||||
alertSize === "small"
|
||||
? "-my-2 -mr-3 ml-auto flex-shrink-0"
|
||||
: "col-start-2 row-span-2 row-start-1 flex items-center justify-center"
|
||||
)}>
|
||||
<Button ref={ref} variant={buttonVariant} size={buttonSize} className={className} {...props}>
|
||||
{children}
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
AlertButton.displayName = "AlertButton";
|
||||
|
||||
// Export the new component
|
||||
export { Alert, AlertTitle, AlertDescription, AlertButton };
|
||||
|
||||
62
apps/web/modules/ui/components/alert/stories.test.tsx
Normal file
62
apps/web/modules/ui/components/alert/stories.test.tsx
Normal file
@@ -0,0 +1,62 @@
|
||||
import { cleanup, render, screen } from "@testing-library/react";
|
||||
import { afterEach, describe, expect, it } from "vitest";
|
||||
import { Default, Error, Info, Small, Success, Warning, withButtonAndIcon } from "./stories";
|
||||
|
||||
describe("Alert Stories", () => {
|
||||
const renderStory = (Story: any) => {
|
||||
return render(Story.render(Story.args));
|
||||
};
|
||||
|
||||
afterEach(() => {
|
||||
cleanup();
|
||||
});
|
||||
|
||||
it("renders Default story", () => {
|
||||
renderStory(Default);
|
||||
expect(screen.getByText("Alert Title")).toBeInTheDocument();
|
||||
expect(screen.getByText("This is an important notification.")).toBeInTheDocument();
|
||||
expect(screen.queryByRole("button")).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("renders Small story", () => {
|
||||
renderStory(Small);
|
||||
expect(screen.getByText("Information Alert")).toBeInTheDocument();
|
||||
expect(screen.getByRole("button")).toBeInTheDocument();
|
||||
expect(screen.getByText("Learn more")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("renders withButtonAndIcon story", () => {
|
||||
renderStory(withButtonAndIcon);
|
||||
expect(screen.getByText("Alert Title")).toBeInTheDocument();
|
||||
expect(screen.getByRole("button")).toBeInTheDocument();
|
||||
expect(screen.getByText("Learn more")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("renders Error story", () => {
|
||||
renderStory(Error);
|
||||
expect(screen.getByText("Error Alert")).toBeInTheDocument();
|
||||
expect(screen.getByText("Your session has expired. Please log in again.")).toBeInTheDocument();
|
||||
expect(screen.getByText("Log in")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("renders Warning story", () => {
|
||||
renderStory(Warning);
|
||||
expect(screen.getByText("Warning Alert")).toBeInTheDocument();
|
||||
expect(screen.getByText("You are editing sensitive data. Be cautious")).toBeInTheDocument();
|
||||
expect(screen.getByText("Proceed")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("renders Info story", () => {
|
||||
renderStory(Info);
|
||||
expect(screen.getByText("Info Alert")).toBeInTheDocument();
|
||||
expect(screen.getByText("There was an update to your application.")).toBeInTheDocument();
|
||||
expect(screen.getByText("Refresh")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("renders Success story", () => {
|
||||
renderStory(Success);
|
||||
expect(screen.getByText("Success Alert")).toBeInTheDocument();
|
||||
expect(screen.getByText("This worked! Please proceed.")).toBeInTheDocument();
|
||||
expect(screen.getByText("Close")).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
@@ -1,49 +1,252 @@
|
||||
import type { Meta, StoryObj } from "@storybook/react";
|
||||
import { AlertCircle } from "lucide-react";
|
||||
import { Alert, AlertDescription, AlertTitle } from "./index";
|
||||
import { Meta, StoryObj } from "@storybook/react";
|
||||
import { LightbulbIcon } from "lucide-react";
|
||||
import { Alert, AlertButton, AlertDescription, AlertTitle } from "./index";
|
||||
|
||||
const meta = {
|
||||
title: "ui/Alert",
|
||||
// We'll define the story options separately from the component props
|
||||
interface StoryOptions {
|
||||
title: string;
|
||||
description: string;
|
||||
showIcon: boolean;
|
||||
showButton: boolean;
|
||||
actionButtonText: string;
|
||||
}
|
||||
|
||||
type StoryProps = React.ComponentProps<typeof Alert> & StoryOptions;
|
||||
|
||||
const meta: Meta<StoryProps> = {
|
||||
title: "UI/Alert",
|
||||
component: Alert,
|
||||
tags: ["autodocs"],
|
||||
argTypes: {
|
||||
variant: {
|
||||
control: "radio",
|
||||
options: ["default", "error"],
|
||||
parameters: {
|
||||
controls: {
|
||||
sort: "requiredFirst",
|
||||
exclude: [],
|
||||
},
|
||||
},
|
||||
args: {
|
||||
variant: "default",
|
||||
// These argTypes are for story controls, not component props
|
||||
argTypes: {
|
||||
variant: {
|
||||
control: "select",
|
||||
options: ["default", "error", "warning", "info", "success"],
|
||||
description: "Style variant of the alert",
|
||||
table: {
|
||||
category: "Appearance",
|
||||
type: { summary: "string" },
|
||||
defaultValue: { summary: "default" },
|
||||
},
|
||||
order: 1,
|
||||
},
|
||||
size: {
|
||||
control: "select",
|
||||
options: ["default", "small"],
|
||||
description: "Size of the alert component",
|
||||
table: {
|
||||
category: "Appearance",
|
||||
type: { summary: "string" },
|
||||
defaultValue: { summary: "default" },
|
||||
},
|
||||
order: 2,
|
||||
},
|
||||
showIcon: {
|
||||
control: "boolean",
|
||||
description: "Whether to show an icon",
|
||||
table: {
|
||||
category: "Appearance",
|
||||
type: { summary: "boolean" },
|
||||
},
|
||||
order: 3,
|
||||
},
|
||||
showButton: {
|
||||
control: "boolean",
|
||||
description: "Whether to show action buttons",
|
||||
table: {
|
||||
category: "Appearance",
|
||||
type: { summary: "boolean" },
|
||||
},
|
||||
order: 4,
|
||||
},
|
||||
title: {
|
||||
control: "text",
|
||||
description: "Alert title text",
|
||||
table: {
|
||||
category: "Content",
|
||||
type: { summary: "string" },
|
||||
},
|
||||
order: 1,
|
||||
},
|
||||
description: {
|
||||
control: "text",
|
||||
description: "Alert description text",
|
||||
table: {
|
||||
category: "Content",
|
||||
type: { summary: "string" },
|
||||
},
|
||||
order: 2,
|
||||
},
|
||||
actionButtonText: {
|
||||
control: "text",
|
||||
description: "Text for the action button",
|
||||
table: {
|
||||
category: "Content",
|
||||
type: { summary: "string" },
|
||||
},
|
||||
order: 2,
|
||||
},
|
||||
},
|
||||
render: (args) => (
|
||||
<Alert {...args}>
|
||||
<AlertTitle>This is an alert</AlertTitle>
|
||||
<AlertDescription>This is a description</AlertDescription>
|
||||
</Alert>
|
||||
),
|
||||
} satisfies Meta<typeof Alert>;
|
||||
};
|
||||
|
||||
export default meta;
|
||||
|
||||
type Story = StoryObj<typeof meta>;
|
||||
// Our story type just specifies Alert props plus our story options
|
||||
type Story = StoryObj<typeof Alert> & { args: StoryOptions };
|
||||
|
||||
export const Default: Story = {};
|
||||
// Create a common render function to reduce duplication
|
||||
const renderAlert = (args: StoryProps) => {
|
||||
// Extract component props
|
||||
const { variant = "default", size = "default", className = "" } = args;
|
||||
|
||||
export const Error: Story = {
|
||||
args: {
|
||||
variant: "error",
|
||||
},
|
||||
};
|
||||
// Extract story content options
|
||||
const {
|
||||
title = "",
|
||||
description = "",
|
||||
showIcon = false,
|
||||
showButton = false,
|
||||
actionButtonText = "",
|
||||
} = args as StoryOptions;
|
||||
|
||||
export const WithIcon: Story = {
|
||||
args: {
|
||||
variant: "error",
|
||||
},
|
||||
render: (args) => (
|
||||
<Alert {...args}>
|
||||
<AlertCircle className="h-4 w-4" />
|
||||
<AlertTitle>This is an alert</AlertTitle>
|
||||
<AlertDescription>This is a description</AlertDescription>
|
||||
return (
|
||||
<Alert variant={variant} size={size} className={className}>
|
||||
{showIcon && <LightbulbIcon />}
|
||||
<AlertTitle className={showIcon ? "pl-7" : ""}>{title}</AlertTitle>
|
||||
{description && <AlertDescription className={showIcon ? "pl-7" : ""}>{description}</AlertDescription>}
|
||||
{showButton && <AlertButton onClick={() => alert("Button clicked")}>{actionButtonText}</AlertButton>}
|
||||
</Alert>
|
||||
),
|
||||
);
|
||||
};
|
||||
|
||||
// Basic example with direct props
|
||||
export const Default: Story = {
|
||||
render: renderAlert,
|
||||
args: {
|
||||
variant: "default",
|
||||
showIcon: false,
|
||||
showButton: false,
|
||||
title: "Alert Title",
|
||||
description: "This is an important notification.",
|
||||
actionButtonText: "Learn more",
|
||||
},
|
||||
};
|
||||
|
||||
// Small size example
|
||||
export const Small: Story = {
|
||||
render: renderAlert,
|
||||
args: {
|
||||
variant: "default",
|
||||
size: "small",
|
||||
title: "Information Alert",
|
||||
description: "This is an important notification.",
|
||||
showIcon: false,
|
||||
showButton: true,
|
||||
actionButtonText: "Learn more",
|
||||
},
|
||||
parameters: {
|
||||
docs: {
|
||||
description: {
|
||||
story: "Use if space is limited or the alert is not the main focus.",
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
// With custom icon
|
||||
export const withButtonAndIcon: Story = {
|
||||
render: renderAlert,
|
||||
args: {
|
||||
variant: "default",
|
||||
title: "Alert Title",
|
||||
description: "This is an important notification.",
|
||||
showIcon: true,
|
||||
showButton: true,
|
||||
actionButtonText: "Learn more",
|
||||
},
|
||||
};
|
||||
|
||||
// Error variant
|
||||
export const Error: Story = {
|
||||
render: renderAlert,
|
||||
args: {
|
||||
variant: "error",
|
||||
title: "Error Alert",
|
||||
description: "Your session has expired. Please log in again.",
|
||||
showIcon: false,
|
||||
showButton: true,
|
||||
actionButtonText: "Log in",
|
||||
},
|
||||
parameters: {
|
||||
docs: {
|
||||
description: {
|
||||
story: "Only use if the user needs to take immediate action or there is a critical error.",
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
// Warning variant
|
||||
export const Warning: Story = {
|
||||
render: renderAlert,
|
||||
args: {
|
||||
variant: "warning",
|
||||
title: "Warning Alert",
|
||||
description: "You are editing sensitive data. Be cautious",
|
||||
showIcon: false,
|
||||
showButton: true,
|
||||
actionButtonText: "Proceed",
|
||||
},
|
||||
parameters: {
|
||||
docs: {
|
||||
description: {
|
||||
story: "Use this to make the user aware of potential issues.",
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
// Info variant
|
||||
export const Info: Story = {
|
||||
render: renderAlert,
|
||||
args: {
|
||||
variant: "info",
|
||||
title: "Info Alert",
|
||||
description: "There was an update to your application.",
|
||||
showIcon: false,
|
||||
showButton: true,
|
||||
actionButtonText: "Refresh",
|
||||
},
|
||||
parameters: {
|
||||
docs: {
|
||||
description: {
|
||||
story: "Use this to give contextual information and support the user.",
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
// Success variant
|
||||
export const Success: Story = {
|
||||
render: renderAlert,
|
||||
args: {
|
||||
variant: "success",
|
||||
title: "Success Alert",
|
||||
description: "This worked! Please proceed.",
|
||||
showIcon: false,
|
||||
showButton: true,
|
||||
actionButtonText: "Close",
|
||||
},
|
||||
parameters: {
|
||||
docs: {
|
||||
description: {
|
||||
story: "Use this to give positive feedback.",
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
const colors = require("tailwindcss/colors");
|
||||
|
||||
module.exports = {
|
||||
content: [
|
||||
// app content
|
||||
@@ -39,7 +41,7 @@ module.exports = {
|
||||
dark: "#00C4B8",
|
||||
},
|
||||
focus: "var(--formbricks-focus, #1982fc)",
|
||||
error: "rgb(from var(--formbricks-error) r g b / <alpha-value>)",
|
||||
// error: "rgb(from var(--formbricks-error) r g b / <alpha-value>)",
|
||||
brandnew: "var(--formbricks-brand, #038178)",
|
||||
primary: {
|
||||
DEFAULT: "#0f172a",
|
||||
@@ -57,6 +59,34 @@ module.exports = {
|
||||
DEFAULT: "#f4f6f8", // light gray background
|
||||
foreground: "#0f172a", // same as primary default for consistency
|
||||
},
|
||||
info: {
|
||||
DEFAULT: colors.blue[600],
|
||||
foreground: colors.blue[900],
|
||||
muted: colors.blue[700],
|
||||
background: colors.blue[50],
|
||||
"background-muted": colors.blue[100],
|
||||
},
|
||||
warning: {
|
||||
DEFAULT: colors.amber[500],
|
||||
foreground: colors.amber[900],
|
||||
muted: colors.amber[700],
|
||||
background: colors.amber[50],
|
||||
"background-muted": colors.amber[100],
|
||||
},
|
||||
success: {
|
||||
DEFAULT: colors.green[600],
|
||||
foreground: colors.green[900],
|
||||
muted: colors.green[700],
|
||||
background: colors.green[50],
|
||||
"background-muted": colors.green[100],
|
||||
},
|
||||
error: {
|
||||
DEFAULT: colors.red[600],
|
||||
foreground: colors.red[900],
|
||||
muted: colors.red[700],
|
||||
background: colors.red[50],
|
||||
"background-muted": colors.red[100],
|
||||
},
|
||||
},
|
||||
keyframes: {
|
||||
fadeIn: {
|
||||
|
||||
@@ -26,6 +26,7 @@ export default defineConfig({
|
||||
"modules/email/components/email-template.tsx",
|
||||
"modules/email/emails/survey/follow-up.tsx",
|
||||
"modules/ui/components/post-hog-client/*.tsx",
|
||||
"modules/ui/components/alert/*.tsx",
|
||||
"app/(app)/environments/**/layout.tsx",
|
||||
"app/(app)/environments/**/settings/(organization)/general/page.tsx",
|
||||
"app/(app)/environments/**/components/PosthogIdentify.tsx",
|
||||
|
||||
Reference in New Issue
Block a user