diff --git a/packages/web/app/src/components/organization/billing/ProPlanBillingWarm.tsx b/packages/web/app/src/components/organization/billing/ProPlanBillingWarm.tsx
index e5931f30d..5186d4a0c 100644
--- a/packages/web/app/src/components/organization/billing/ProPlanBillingWarm.tsx
+++ b/packages/web/app/src/components/organization/billing/ProPlanBillingWarm.tsx
@@ -1,5 +1,5 @@
import { ReactElement } from 'react';
-import { Callout } from '@/components/v2';
+import { Callout } from '@/components/ui/callout';
import { FragmentType, graphql, useFragment } from '@/gql';
const ProPlanBilling_OrganizationFragment = graphql(`
diff --git a/packages/web/app/src/components/organization/billing/RateLimitWarn.tsx b/packages/web/app/src/components/organization/billing/RateLimitWarn.tsx
index 2045c7708..938a0ab70 100644
--- a/packages/web/app/src/components/organization/billing/RateLimitWarn.tsx
+++ b/packages/web/app/src/components/organization/billing/RateLimitWarn.tsx
@@ -1,5 +1,5 @@
import { ReactElement } from 'react';
-import { Callout } from '@/components/v2';
+import { Callout } from '@/components/ui/callout';
import { FragmentType, graphql, useFragment } from '@/gql';
import { BillingPlanType } from '@/gql/graphql';
diff --git a/packages/web/app/src/components/policy/policy-settings.tsx b/packages/web/app/src/components/policy/policy-settings.tsx
index d69afcd27..623956b60 100644
--- a/packages/web/app/src/components/policy/policy-settings.tsx
+++ b/packages/web/app/src/components/policy/policy-settings.tsx
@@ -9,7 +9,8 @@ import {
SchemaPolicyInput,
} from '@/gql/graphql';
import type { ResultOf } from '@graphql-typed-document-node/core';
-import { Callout, DataWrapper } from '../v2';
+import { Callout } from '../ui/callout';
+import { DataWrapper } from '../v2';
import { PolicyListItem } from './policy-list-item';
import { buildValidationSchema, PolicyFormValues } from './rules-configuration';
diff --git a/packages/web/app/src/components/target/laboratory/create-collection-modal.tsx b/packages/web/app/src/components/target/laboratory/create-collection-modal.tsx
index 6888e6832..c8c00cc0e 100644
--- a/packages/web/app/src/components/target/laboratory/create-collection-modal.tsx
+++ b/packages/web/app/src/components/target/laboratory/create-collection-modal.tsx
@@ -3,6 +3,7 @@ import { useForm, UseFormReturn } from 'react-hook-form';
import { useMutation, useQuery } from 'urql';
import { z } from 'zod';
import { Button } from '@/components/ui/button';
+import { Callout } from '@/components/ui/callout';
import {
Dialog,
DialogContent,
@@ -20,7 +21,6 @@ import {
FormMessage,
} from '@/components/ui/form';
import { Input } from '@/components/ui/input';
-import { Callout } from '@/components/v2';
import { graphql } from '@/gql';
import { zodResolver } from '@hookform/resolvers/zod';
diff --git a/packages/web/app/src/components/ui/callout.tsx b/packages/web/app/src/components/ui/callout.tsx
new file mode 100644
index 000000000..c9c65f61b
--- /dev/null
+++ b/packages/web/app/src/components/ui/callout.tsx
@@ -0,0 +1,65 @@
+import React from 'react';
+import { cva, type VariantProps } from 'class-variance-authority';
+import { cn } from '@/lib/utils';
+import {
+ CrossCircledIcon,
+ ExclamationTriangleIcon,
+ InfoCircledIcon,
+ LightningBoltIcon,
+} from '@radix-ui/react-icons';
+
+const TypeToEmoji = {
+ default: ,
+ error: ,
+ info: ,
+ warning: ,
+};
+
+type CalloutType = keyof typeof TypeToEmoji;
+
+const calloutVariants = cva('mt-6 flex items-center gap-4 rounded-lg border px-4 py-2', {
+ variants: {
+ type: {
+ default:
+ 'border-orange-100 bg-orange-50 text-orange-800 dark:border-orange-400/30 dark:bg-orange-400/20 dark:text-orange-300',
+ error:
+ 'border-red-200 bg-red-100 text-red-900 dark:border-red-200/30 dark:bg-red-900/30 dark:text-red-200',
+ info: 'border-blue-200 bg-blue-100 text-blue-900 dark:border-blue-200/30 dark:bg-blue-900/30 dark:text-blue-200',
+ warning:
+ 'border-yellow-100 bg-yellow-50 text-yellow-900 dark:border-yellow-200/30 dark:bg-yellow-700/30 dark:text-yellow-200',
+ },
+ },
+ defaultVariants: {
+ type: 'default',
+ },
+});
+
+type CalloutProps = {
+ type?: CalloutType;
+ emoji?: string | React.ReactElement;
+ children: React.ReactNode;
+ className?: string;
+};
+
+const Callout = React.forwardRef<
+ HTMLDivElement,
+ CalloutProps & VariantProps
+>(({ children, type = 'default', emoji = TypeToEmoji[type], className, ...props }, ref) => {
+ return (
+
+
+ {emoji}
+
+
{children}
+
+ );
+});
+
+Callout.displayName = 'Callout';
+
+export { Callout };
diff --git a/packages/web/app/src/components/v2/callout.tsx b/packages/web/app/src/components/v2/callout.tsx
deleted file mode 100644
index a99421784..000000000
--- a/packages/web/app/src/components/v2/callout.tsx
+++ /dev/null
@@ -1,61 +0,0 @@
-import type { ReactElement, ReactNode } from 'react';
-import { cn } from '@/lib/utils';
-import { InfoCircledIcon } from '@radix-ui/react-icons';
-
-const TypeToEmoji = {
- default: '💡',
- error: '🚫',
- info: ,
- warning: '⚠️',
-};
-
-type CalloutType = keyof typeof TypeToEmoji;
-
-const classes: Record = {
- default: cn(
- 'border-orange-100 bg-orange-50 text-orange-800 dark:border-orange-400/30 dark:bg-orange-400/20 dark:text-orange-300',
- ),
- error: cn(
- 'border-red-200 bg-red-100 text-red-900 dark:border-red-200/30 dark:bg-red-900/30 dark:text-red-200',
- ),
- info: cn(
- 'border-blue-200 bg-blue-100 text-blue-900 dark:border-blue-200/30 dark:bg-blue-900/30 dark:text-blue-200',
- ),
- warning: cn(
- 'border-yellow-100 bg-yellow-50 text-yellow-900 dark:border-yellow-200/30 dark:bg-yellow-700/30 dark:text-yellow-200',
- ),
-};
-
-type CalloutProps = {
- type?: CalloutType;
- emoji?: string | ReactElement;
- children: ReactNode;
- className?: string;
-};
-
-export function Callout({
- children,
- type = 'default',
- emoji = TypeToEmoji[type],
- className,
-}: CalloutProps): ReactElement {
- return (
-
-
- {emoji}
-
-
{children}
-
- );
-}
diff --git a/packages/web/app/src/components/v2/index.ts b/packages/web/app/src/components/v2/index.ts
index 8754d1243..11b3de0e4 100644
--- a/packages/web/app/src/components/v2/index.ts
+++ b/packages/web/app/src/components/v2/index.ts
@@ -1,7 +1,6 @@
export { Accordion } from '@/components/v2/accordion';
export { Autocomplete } from '@/components/v2/autocomplete';
export { Avatar } from '@/components/v2/avatar';
-export { Callout } from '@/components/v2/callout';
export { Card } from '@/components/v2/card';
export { Checkbox } from '@/components/v2/checkbox';
export { CopyValue } from '@/components/v2/copy-value';
diff --git a/packages/web/app/src/stories/callout.stories.tsx b/packages/web/app/src/stories/callout.stories.tsx
index ece0e1ffc..f903b6248 100644
--- a/packages/web/app/src/stories/callout.stories.tsx
+++ b/packages/web/app/src/stories/callout.stories.tsx
@@ -1,37 +1,67 @@
-import type { Meta, StoryObj } from '@storybook/react';
-import { Callout } from '../components/v2/callout';
+import { Callout } from '@/components/ui/callout';
+import {
+ CrossCircledIcon,
+ ExclamationTriangleIcon,
+ InfoCircledIcon,
+ LightningBoltIcon,
+} from '@radix-ui/react-icons';
+import { Meta, StoryObj } from '@storybook/react';
const meta: Meta = {
- title: 'Callout',
+ title: 'Components/Callout',
component: Callout,
+ argTypes: {
+ type: {
+ control: { type: 'inline-radio' },
+ options: ['default', 'error', 'info', 'warning'],
+ },
+ emoji: {
+ control: { type: 'text' },
+ },
+ children: {
+ control: { type: 'text' },
+ },
+ className: {
+ control: { type: 'text' },
+ },
+ },
+ args: {
+ type: 'default',
+ children: 'This is a callout',
+ },
};
export default meta;
+
type Story = StoryObj;
export const Default: Story = {
- render: () => Hello,
+ args: {
+ type: 'default',
+ emoji: ,
+ },
};
export const Error: Story = {
- render: () => Hello,
+ args: {
+ type: 'error',
+ emoji: ,
+ children: 'This is an error callout',
+ },
};
export const Info: Story = {
- render: () => Hello,
+ args: {
+ type: 'info',
+ emoji: ,
+ children: 'This is an info callout',
+ },
};
export const Warning: Story = {
- render: () => Hello,
-};
-
-export const Long: Story = {
- render: () => (
-
- Your organization is being rate-limited for operations.
-
- Since you reached your organization rate-limit and data ingestion limitation, your
- organization The Guild is currently unable to ingest data.
-
- ),
+ args: {
+ type: 'warning',
+ emoji: ,
+ children: 'This is a warning callout',
+ },
};