Compare commits

...

2 Commits

Author SHA1 Message Date
Otto
2ed08aeaa7 fix: address review feedback on CoPilot storybook stories 2026-02-19 21:59:46 +00:00
Otto
34e8fa6e5a feat: Add Storybook stories for CoPilot components
Add 19 story files covering CoPilot presentational and lightly-mockable
components, plus a shared CopilotChatActions mock decorator.

Tier 1 (pure presentational):
- OrbitLoader, PulseLoader, ScaleLoader, SpinnerLoader
- ProgressBar, MorphingTextAnimation
- AccordionContent sub-components
- ErrorCard (RunBlock + RunAgent)
- BlockOutputCard, ExecutionStartedCard

Tier 2 (light mocking):
- ChatInput, ToolAccordion
- MobileHeader, MobileDrawer
- ClarificationQuestionsCard
- AgentDetailsCard, SetupRequirementsCard (RunBlock + RunAgent)

Also updates .storybook/main.ts to include copilot paths.

Refs: SECRT-2018
2026-02-19 17:50:52 +00:00
22 changed files with 1051 additions and 2 deletions

View File

@@ -6,6 +6,7 @@ const config: StorybookConfig = {
"../src/components/tokens/**/*.stories.@(js|jsx|mjs|ts|tsx)",
"../src/components/atoms/**/*.stories.@(js|jsx|mjs|ts|tsx)",
"../src/components/molecules/**/*.stories.@(js|jsx|mjs|ts|tsx)",
"../src/app/(platform)/copilot/**/*.stories.@(js|jsx|mjs|ts|tsx)",
],
addons: [
"@storybook/addon-a11y",

View File

@@ -0,0 +1,47 @@
import type { Meta, StoryObj } from "@storybook/nextjs";
import { fn } from "@storybook/test";
import { ChatInput } from "./ChatInput";
const meta: Meta<typeof ChatInput> = {
title: "CoPilot/Chat/ChatInput",
component: ChatInput,
tags: ["autodocs"],
parameters: {
layout: "padded",
docs: {
description: {
component:
"Chat message input with send button, voice recording, and streaming stop support.",
},
},
},
decorators: [
(Story) => (
<div className="max-w-[600px]">
<Story />
</div>
),
],
args: {
onSend: fn(),
},
};
export default meta;
type Story = StoryObj<typeof ChatInput>;
export const Default: Story = {};
export const Disabled: Story = {
args: { disabled: true },
};
export const Streaming: Story = {
args: {
isStreaming: true,
onStop: fn(),
},
};
export const WithPlaceholder: Story = {
args: { placeholder: "Ask me anything about your agents..." },
};

View File

@@ -0,0 +1,73 @@
import type { Meta, StoryObj } from "@storybook/nextjs";
import type { SessionSummaryResponse } from "@/app/api/__generated__/models/sessionSummaryResponse";
import { MobileDrawer } from "./MobileDrawer";
const mockSessions: SessionSummaryResponse[] = [
{
id: "session-1",
title: "Help me build a weather agent",
created_at: new Date().toISOString(),
updated_at: new Date().toISOString(),
},
{
id: "session-2",
title: "Debug my email block",
created_at: new Date(Date.now() - 86400000).toISOString(),
updated_at: new Date(Date.now() - 86400000).toISOString(),
},
{
id: "session-3",
title: "Search for image generation blocks",
created_at: new Date(Date.now() - 86400000 * 3).toISOString(),
updated_at: new Date(Date.now() - 86400000 * 3).toISOString(),
},
];
const noop = () => {};
const meta: Meta<typeof MobileDrawer> = {
title: "CoPilot/Chat/MobileDrawer",
component: MobileDrawer,
tags: ["autodocs"],
parameters: {
layout: "fullscreen",
docs: {
description: {
component:
"Slide-out drawer for mobile showing the list of chat sessions.",
},
},
},
args: {
isOpen: true,
currentSessionId: null,
isLoading: false,
sessions: [],
onSelectSession: noop,
onNewChat: noop,
onClose: noop,
onOpenChange: noop,
},
};
export default meta;
type Story = StoryObj<typeof MobileDrawer>;
export const Empty: Story = {};
export const WithSessions: Story = {
args: {
sessions: mockSessions,
currentSessionId: "session-1",
},
};
export const Loading: Story = {
args: { isLoading: true },
};
export const WithCurrentSession: Story = {
args: {
sessions: mockSessions,
currentSessionId: "session-2",
},
};

View File

@@ -0,0 +1,25 @@
import type { Meta, StoryObj } from "@storybook/nextjs";
import { fn } from "@storybook/test";
import { MobileHeader } from "./MobileHeader";
const meta: Meta<typeof MobileHeader> = {
title: "CoPilot/Chat/MobileHeader",
component: MobileHeader,
tags: ["autodocs"],
parameters: {
layout: "fullscreen",
docs: {
description: {
component:
"Mobile-only header button that opens the session drawer on small screens.",
},
},
},
args: {
onOpenDrawer: fn(),
},
};
export default meta;
type Story = StoryObj<typeof MobileHeader>;
export const Default: Story = {};

View File

@@ -0,0 +1,31 @@
import type { Meta, StoryObj } from "@storybook/nextjs";
import { MorphingTextAnimation } from "./MorphingTextAnimation";
const meta: Meta<typeof MorphingTextAnimation> = {
title: "CoPilot/Loaders/MorphingTextAnimation",
component: MorphingTextAnimation,
tags: ["autodocs"],
parameters: {
layout: "centered",
docs: {
description: {
component:
"Animated text that morphs in letter by letter using framer-motion.",
},
},
},
};
export default meta;
type Story = StoryObj<typeof MorphingTextAnimation>;
export const Short: Story = {
args: { text: "Loading..." },
};
export const Long: Story = {
args: { text: "Analyzing your request and preparing the best response" },
};
export const Emoji: Story = {
args: { text: "🚀 Building your agent..." },
};

View File

@@ -0,0 +1,28 @@
import type { Meta, StoryObj } from "@storybook/nextjs";
import { OrbitLoader } from "./OrbitLoader";
const meta: Meta<typeof OrbitLoader> = {
title: "CoPilot/Loaders/OrbitLoader",
component: OrbitLoader,
tags: ["autodocs"],
parameters: {
layout: "centered",
docs: {
description: {
component: "Animated orbiting ball loader used during CoPilot actions.",
},
},
},
};
export default meta;
type Story = StoryObj<typeof OrbitLoader>;
export const Default: Story = {};
export const Small: Story = {
args: { size: 16 },
};
export const Large: Story = {
args: { size: 48 },
};

View File

@@ -0,0 +1,46 @@
import type { Meta, StoryObj } from "@storybook/nextjs";
import { ProgressBar } from "./ProgressBar";
const meta: Meta<typeof ProgressBar> = {
title: "CoPilot/Loaders/ProgressBar",
component: ProgressBar,
tags: ["autodocs"],
parameters: {
layout: "padded",
docs: {
description: {
component:
"Horizontal progress bar with label and percentage indicator.",
},
},
},
decorators: [
(Story) => (
<div className="w-80">
<Story />
</div>
),
],
};
export default meta;
type Story = StoryObj<typeof ProgressBar>;
export const Empty: Story = {
args: { value: 0 },
};
export const Partial: Story = {
args: { value: 42 },
};
export const Full: Story = {
args: { value: 100 },
};
export const CustomLabel: Story = {
args: { value: 65, label: "Uploading files..." },
};
export const OverBounds: Story = {
args: { value: 150 },
};

View File

@@ -0,0 +1,28 @@
import type { Meta, StoryObj } from "@storybook/nextjs";
import { PulseLoader } from "./PulseLoader";
const meta: Meta<typeof PulseLoader> = {
title: "CoPilot/Loaders/PulseLoader",
component: PulseLoader,
tags: ["autodocs"],
parameters: {
layout: "centered",
docs: {
description: {
component: "Pulsing circle loader animation.",
},
},
},
};
export default meta;
type Story = StoryObj<typeof PulseLoader>;
export const Default: Story = {};
export const Small: Story = {
args: { size: 16 },
};
export const Large: Story = {
args: { size: 48 },
};

View File

@@ -0,0 +1,28 @@
import type { Meta, StoryObj } from "@storybook/nextjs";
import { ScaleLoader } from "./ScaleLoader";
const meta: Meta<typeof ScaleLoader> = {
title: "CoPilot/Loaders/ScaleLoader",
component: ScaleLoader,
tags: ["autodocs"],
parameters: {
layout: "centered",
docs: {
description: {
component: "Scaling bar loader animation.",
},
},
},
};
export default meta;
type Story = StoryObj<typeof ScaleLoader>;
export const Default: Story = {};
export const Small: Story = {
args: { size: 24 },
};
export const Large: Story = {
args: { size: 72 },
};

View File

@@ -0,0 +1,28 @@
import type { Meta, StoryObj } from "@storybook/nextjs";
import { SpinnerLoader } from "./SpinnerLoader";
const meta: Meta<typeof SpinnerLoader> = {
title: "CoPilot/Loaders/SpinnerLoader",
component: SpinnerLoader,
tags: ["autodocs"],
parameters: {
layout: "centered",
docs: {
description: {
component: "Classic spinning loader animation.",
},
},
},
};
export default meta;
type Story = StoryObj<typeof SpinnerLoader>;
export const Default: Story = {};
export const Small: Story = {
args: { size: 16 },
};
export const Large: Story = {
args: { size: 48 },
};

View File

@@ -0,0 +1,107 @@
import type { Meta, StoryObj } from "@storybook/nextjs";
import {
ContentBadge,
ContentCard,
ContentCardDescription,
ContentCardHeader,
ContentCardSubtitle,
ContentCardTitle,
ContentCodeBlock,
ContentGrid,
ContentHint,
ContentLink,
ContentMessage,
ContentSuggestionsList,
} from "./AccordionContent";
const meta: Meta = {
title: "CoPilot/Content/AccordionContent",
tags: ["autodocs"],
parameters: {
layout: "padded",
docs: {
description: {
component:
"Building-block components used inside ToolAccordion panels to render structured content.",
},
},
},
decorators: [
(Story) => (
<div className="max-w-[480px]">
<Story />
</div>
),
],
};
export default meta;
export const Grid: StoryObj = {
render: () => (
<ContentGrid>
<ContentMessage>First item in the grid</ContentMessage>
<ContentMessage>Second item in the grid</ContentMessage>
</ContentGrid>
),
};
export const Card: StoryObj = {
render: () => (
<ContentCard>
<ContentCardHeader>
<ContentCardTitle>Weather Block</ContentCardTitle>
</ContentCardHeader>
<ContentCardSubtitle>get_weather_v2</ContentCardSubtitle>
<ContentCardDescription>
Fetches current weather data for a given location using the OpenWeather
API.
</ContentCardDescription>
</ContentCard>
),
};
export const Message: StoryObj = {
render: () => (
<ContentMessage>
Your agent has been created and is ready to run.
</ContentMessage>
),
};
export const CodeBlock: StoryObj = {
render: () => (
<ContentCodeBlock>
{JSON.stringify({ location: "London", units: "metric" }, null, 2)}
</ContentCodeBlock>
),
};
export const BadgeAndLink: StoryObj = {
render: () => (
<div className="flex items-center gap-2">
<ContentBadge>Required</ContentBadge>
<ContentBadge>Optional</ContentBadge>
<ContentLink href="https://docs.agpt.co">View Docs</ContentLink>
</div>
),
};
export const Hint: StoryObj = {
render: () => (
<ContentHint>
Tip: You can pass workspace:// references as input values.
</ContentHint>
),
};
export const SuggestionsList: StoryObj = {
render: () => (
<ContentSuggestionsList
items={[
"Try running with different inputs",
"Check the block documentation",
"Ensure your API key is configured",
]}
/>
),
};

View File

@@ -0,0 +1,58 @@
import type { Meta, StoryObj } from "@storybook/nextjs";
import { GearIcon, LightningIcon } from "@phosphor-icons/react";
import { ToolAccordion } from "./ToolAccordion";
import { ContentMessage } from "./AccordionContent";
const meta: Meta<typeof ToolAccordion> = {
title: "CoPilot/Tools/ToolAccordion",
component: ToolAccordion,
tags: ["autodocs"],
parameters: {
layout: "padded",
docs: {
description: {
component:
"Collapsible accordion used to wrap tool invocation results in the CoPilot chat.",
},
},
},
decorators: [
(Story) => (
<div className="max-w-[480px]">
<Story />
</div>
),
],
};
export default meta;
type Story = StoryObj<typeof ToolAccordion>;
export const Collapsed: Story = {
args: {
icon: <GearIcon width={16} height={16} />,
title: "Running GetWeather block",
children: <ContentMessage>Block executed successfully.</ContentMessage>,
defaultExpanded: false,
},
};
export const Expanded: Story = {
args: {
icon: <LightningIcon width={16} height={16} />,
title: "Agent execution started",
children: (
<ContentMessage>The agent is now processing your request.</ContentMessage>
),
defaultExpanded: true,
},
};
export const WithDescription: Story = {
args: {
icon: <GearIcon width={16} height={16} />,
title: "FindBlocks",
description: "Searching for blocks matching your query...",
children: <ContentMessage>Found 3 matching blocks.</ContentMessage>,
defaultExpanded: true,
},
};

View File

@@ -0,0 +1,9 @@
import type { Decorator } from "@storybook/nextjs";
import { fn } from "@storybook/test";
import { CopilotChatActionsContext } from "../CopilotChatActionsProvider/useCopilotChatActions";
export const withCopilotChatActions: Decorator = (Story) => (
<CopilotChatActionsContext.Provider value={{ onSend: fn() }}>
<Story />
</CopilotChatActionsContext.Provider>
);

View File

@@ -0,0 +1,87 @@
import type { Meta, StoryObj } from "@storybook/nextjs";
import { fn } from "@storybook/test";
import { ClarificationQuestionsCard } from "./ClarificationQuestionsCard";
const meta: Meta<typeof ClarificationQuestionsCard> = {
title: "CoPilot/Tools/CreateAgent/ClarificationQuestionsCard",
component: ClarificationQuestionsCard,
tags: ["autodocs"],
parameters: {
layout: "padded",
docs: {
description: {
component:
"Interactive card that asks clarifying questions before creating an agent.",
},
},
},
decorators: [
(Story) => (
<div className="max-w-[600px]">
<Story />
</div>
),
],
args: {
onSubmitAnswers: fn(),
},
};
export default meta;
type Story = StoryObj<typeof ClarificationQuestionsCard>;
export const SingleQuestion: Story = {
args: {
message: "I need a bit more detail to build the right agent for you.",
questions: [
{
question: "What data source should the agent pull from?",
keyword: "data_source",
example: "e.g. a REST API, a CSV file, or a database",
},
],
},
};
export const MultipleQuestions: Story = {
args: {
message:
"Before I create your agent, I have a few questions to make sure it does exactly what you need.",
questions: [
{
question: "What is the primary goal of this agent?",
keyword: "goal",
example: "e.g. monitor stock prices and send alerts",
},
{
question: "How often should it run?",
keyword: "frequency",
example: "e.g. every hour, once a day",
},
{
question: "Where should it send notifications?",
keyword: "notification_channel",
example: "e.g. email, Slack, Discord",
},
],
},
};
export const Answered: Story = {
args: {
message: "Thanks for your answers!",
questions: [
{
question: "What is the primary goal?",
keyword: "goal",
},
],
isAnswered: true,
},
};
export const EmptyQuestions: Story = {
args: {
message: "No clarification needed — proceeding with defaults.",
questions: [],
},
};

View File

@@ -0,0 +1,66 @@
import type { Meta, StoryObj } from "@storybook/nextjs";
import type { AgentDetailsResponse } from "@/app/api/__generated__/models/agentDetailsResponse";
import { withCopilotChatActions } from "../../../../components/_storybook/CopilotChatActionsDecorator";
import { AgentDetailsCard } from "./AgentDetailsCard";
const meta: Meta<typeof AgentDetailsCard> = {
title: "CoPilot/Tools/RunAgent/AgentDetailsCard",
component: AgentDetailsCard,
tags: ["autodocs"],
parameters: {
layout: "padded",
docs: {
description: {
component:
"Shows agent details with options to run with example values or custom inputs.",
},
},
},
decorators: [
withCopilotChatActions,
(Story) => (
<div className="max-w-[480px]">
<Story />
</div>
),
],
};
export default meta;
type Story = StoryObj<typeof AgentDetailsCard>;
export const Default: Story = {
args: {
output: {
message: "Here are the details for the WeatherReporter agent.",
agent: {
id: "agent-1",
name: "WeatherReporter",
description: "Fetches and reports weather for any city.",
},
user_authenticated: true,
} as AgentDetailsResponse,
},
};
export const WithInputSchema: Story = {
args: {
output: {
message: "Agent found. You can run it with your own inputs.",
agent: {
id: "agent-2",
name: "EmailDrafter",
description: "Drafts professional emails based on your instructions.",
inputs: {
type: "object",
properties: {
recipient: { type: "string", title: "Recipient" },
subject: { type: "string", title: "Subject" },
tone: { type: "string", title: "Tone", default: "professional" },
},
required: ["recipient", "subject"],
},
},
user_authenticated: true,
} as AgentDetailsResponse,
},
};

View File

@@ -0,0 +1,45 @@
import type { Meta, StoryObj } from "@storybook/nextjs";
import type { ErrorResponse } from "@/app/api/__generated__/models/errorResponse";
import { ErrorCard } from "./ErrorCard";
const meta: Meta<typeof ErrorCard> = {
title: "CoPilot/Tools/RunAgent/ErrorCard",
component: ErrorCard,
tags: ["autodocs"],
parameters: {
layout: "padded",
docs: {
description: {
component:
"Displays an error response from an agent execution attempt.",
},
},
},
decorators: [
(Story) => (
<div className="max-w-[480px]">
<Story />
</div>
),
],
};
export default meta;
type Story = StoryObj<typeof ErrorCard>;
export const SimpleError: Story = {
args: {
output: {
message: "Agent not found in the store.",
} as ErrorResponse,
},
};
export const WithDetails: Story = {
args: {
output: {
message: "Agent execution failed.",
error: "RuntimeError: Graph execution timed out.",
details: { graph_id: "graph-xyz", timeout_ms: 30000 },
} as ErrorResponse,
},
};

View File

@@ -0,0 +1,50 @@
import type { Meta, StoryObj } from "@storybook/nextjs";
import type { ExecutionStartedResponse } from "@/app/api/__generated__/models/executionStartedResponse";
import { ExecutionStartedCard } from "./ExecutionStartedCard";
const meta: Meta<typeof ExecutionStartedCard> = {
title: "CoPilot/Tools/RunAgent/ExecutionStartedCard",
component: ExecutionStartedCard,
tags: ["autodocs"],
parameters: {
layout: "padded",
docs: {
description: {
component:
"Confirmation card shown when an agent execution has been successfully started.",
},
},
},
decorators: [
(Story) => (
<div className="max-w-[480px]">
<Story />
</div>
),
],
};
export default meta;
type Story = StoryObj<typeof ExecutionStartedCard>;
export const Default: Story = {
args: {
output: {
execution_id: "exec-abc-123",
graph_id: "graph-xyz-456",
graph_name: "WeatherReporter",
message: "Agent execution started successfully.",
} as ExecutionStartedResponse,
},
};
export const WithLibraryLink: Story = {
args: {
output: {
execution_id: "exec-def-789",
graph_id: "graph-uvw-012",
graph_name: "EmailSender",
message: "Agent execution started. You can monitor progress below.",
library_agent_link: "/library/agents/agent-id-123",
} as ExecutionStartedResponse,
},
};

View File

@@ -0,0 +1,87 @@
import type { Meta, StoryObj } from "@storybook/nextjs";
import type { SetupRequirementsResponse } from "@/app/api/__generated__/models/setupRequirementsResponse";
import { withCopilotChatActions } from "../../../../components/_storybook/CopilotChatActionsDecorator";
import { SetupRequirementsCard } from "./SetupRequirementsCard";
const meta: Meta<typeof SetupRequirementsCard> = {
title: "CoPilot/Tools/RunAgent/SetupRequirementsCard",
component: SetupRequirementsCard,
tags: ["autodocs"],
parameters: {
layout: "padded",
docs: {
description: {
component:
"Displays setup requirements for running an agent, including missing credentials and expected inputs.",
},
},
},
decorators: [
withCopilotChatActions,
(Story) => (
<div className="max-w-[480px]">
<Story />
</div>
),
],
};
export default meta;
type Story = StoryObj<typeof SetupRequirementsCard>;
export const WithCredentials: Story = {
args: {
output: {
message: "This agent requires credentials before it can run.",
setup_info: {
agent_id: "agent-1",
agent_name: "WeatherReporter",
user_readiness: {
missing_credentials: {
openweather_key: {
title: "OpenWeather API Key",
provider: "openweather",
type: "api_key",
required: true,
},
},
},
},
} as SetupRequirementsResponse,
},
};
export const WithInputs: Story = {
args: {
output: {
message: "This agent needs the following inputs to run.",
setup_info: {
agent_id: "agent-2",
agent_name: "EmailDrafter",
requirements: {
inputs: [
{
name: "recipient",
title: "Recipient",
type: "string",
required: true,
description: "Email address of the recipient",
},
{
name: "subject",
title: "Subject",
type: "string",
required: true,
},
{
name: "body_hint",
title: "Body Hint",
type: "string",
required: false,
description: "Optional hint for email body",
},
],
},
},
} as SetupRequirementsResponse,
},
};

View File

@@ -4,7 +4,7 @@ import type { BlockDetailsResponse } from "../../helpers";
import { BlockDetailsCard } from "./BlockDetailsCard";
const meta: Meta<typeof BlockDetailsCard> = {
title: "Copilot/RunBlock/BlockDetailsCard",
title: "CoPilot/Tools/RunBlock/BlockDetailsCard",
component: BlockDetailsCard,
parameters: {
layout: "centered",
@@ -12,7 +12,7 @@ const meta: Meta<typeof BlockDetailsCard> = {
tags: ["autodocs"],
decorators: [
(Story) => (
<div style={{ maxWidth: 480 }}>
<div className="max-w-[480px]">
<Story />
</div>
),

View File

@@ -0,0 +1,65 @@
import type { Meta, StoryObj } from "@storybook/nextjs";
import type { BlockOutputResponse } from "@/app/api/__generated__/models/blockOutputResponse";
import { BlockOutputCard } from "./BlockOutputCard";
const meta: Meta<typeof BlockOutputCard> = {
title: "CoPilot/Tools/RunBlock/BlockOutputCard",
component: BlockOutputCard,
tags: ["autodocs"],
parameters: {
layout: "padded",
docs: {
description: {
component:
"Renders the output of a successful block execution, grouped by output key.",
},
},
},
decorators: [
(Story) => (
<div className="max-w-[480px]">
<Story />
</div>
),
],
};
export default meta;
type Story = StoryObj<typeof BlockOutputCard>;
export const SingleOutput: Story = {
args: {
output: {
block_id: "block-1",
block_name: "GetWeather",
message: "Block executed successfully.",
outputs: {
temperature: [22.5],
},
success: true,
} as BlockOutputResponse,
},
};
export const MultipleOutputs: Story = {
args: {
output: {
block_id: "block-2",
block_name: "SearchWeb",
message: "Found 3 results.",
outputs: {
title: ["Result 1", "Result 2", "Result 3"],
url: [
"https://example.com/1",
"https://example.com/2",
"https://example.com/3",
],
snippet: [
"First result snippet...",
"Second result snippet...",
"Third result snippet...",
],
},
success: true,
} as BlockOutputResponse,
},
};

View File

@@ -0,0 +1,58 @@
import type { Meta, StoryObj } from "@storybook/nextjs";
import type { ErrorResponse } from "@/app/api/__generated__/models/errorResponse";
import { ErrorCard } from "./ErrorCard";
const meta: Meta<typeof ErrorCard> = {
title: "CoPilot/Tools/RunBlock/ErrorCard",
component: ErrorCard,
tags: ["autodocs"],
parameters: {
layout: "padded",
docs: {
description: {
component:
"Displays an error response from a block execution, including optional error and details fields.",
},
},
},
decorators: [
(Story) => (
<div className="max-w-[480px]">
<Story />
</div>
),
],
};
export default meta;
type Story = StoryObj<typeof ErrorCard>;
export const SimpleError: Story = {
args: {
output: {
message: "Block execution failed: timeout after 30 seconds.",
} as ErrorResponse,
},
};
export const WithDetails: Story = {
args: {
output: {
message: "Block execution failed.",
details: {
block_id: "abc-123",
retry_count: 3,
last_attempt: "2026-02-19",
},
} as ErrorResponse,
},
};
export const WithErrorAndDetails: Story = {
args: {
output: {
message: "Authentication failed for GetWeather block.",
error: "InvalidCredentialsError: API key is expired or invalid.",
details: { provider: "openweather", status_code: 401 },
} as ErrorResponse,
},
};

View File

@@ -0,0 +1,82 @@
import type { Meta, StoryObj } from "@storybook/nextjs";
import type { SetupRequirementsResponse } from "@/app/api/__generated__/models/setupRequirementsResponse";
import { withCopilotChatActions } from "../../../../components/_storybook/CopilotChatActionsDecorator";
import { SetupRequirementsCard } from "./SetupRequirementsCard";
const meta: Meta<typeof SetupRequirementsCard> = {
title: "CoPilot/Tools/RunBlock/SetupRequirementsCard",
component: SetupRequirementsCard,
tags: ["autodocs"],
parameters: {
layout: "padded",
docs: {
description: {
component:
"Displays setup requirements for running a block, including missing credentials and expected inputs.",
},
},
},
decorators: [
withCopilotChatActions,
(Story) => (
<div className="max-w-[480px]">
<Story />
</div>
),
],
};
export default meta;
type Story = StoryObj<typeof SetupRequirementsCard>;
export const WithInputs: Story = {
args: {
output: {
message:
"This block requires some inputs before it can run. Please provide the values below.",
setup_info: {
agent_id: "block-1",
agent_name: "GetWeather",
requirements: {
inputs: [
{
name: "location",
title: "Location",
type: "string",
required: true,
description: "City name or coordinates",
},
{
name: "units",
title: "Units",
type: "string",
required: false,
description: "metric or imperial",
},
],
},
},
} as SetupRequirementsResponse,
},
};
export const WithCredentials: Story = {
args: {
output: {
message: "This block requires credentials to be configured.",
setup_info: {
agent_id: "block-2",
agent_name: "SendEmail",
user_readiness: {
missing_credentials: {
smtp_cred: {
title: "SMTP Credentials",
provider: "smtp",
type: "api_key",
required: true,
},
},
},
},
} as SetupRequirementsResponse,
},
};