import type { Meta, StoryObj } from "@storybook/react";
import { useEffect } from "react";
import {
SearchPalette,
SearchProvider,
SearchTrigger,
useSearchContext,
} from "./index";
import type { SearchResult } from "./searchbox-types";
/**
* Mock documents copied verbatim from app/demo/searchbox/page.tsx
*/
const mockDocuments: SearchResult[] = [
{
id: "1",
title: "What is Functions.do",
url: "/docs/apis/functions",
breadcrumbs: ["APIs", "Functions"],
matches: [
{
type: "content",
snippet:
"Introducing Functions.do, a serverless function platform that you can scale.",
},
{
type: "content",
snippet:
"Most serverless platforms can already suffice the needs of simple functions.",
},
{
type: "section",
heading: "Getting Started",
snippet:
"Functions.do provides serverless function execution with automatic scaling...",
},
{
type: "section",
heading: "Authentication",
snippet:
"Use Bearer tokens to authenticate API requests to Functions.do...",
},
],
},
{
id: "2",
title: "Workflows.do",
url: "/docs/apis/workflows",
breadcrumbs: ["APIs", "Workflows"],
matches: [
{
type: "content",
snippet:
"Workflows.do enables complex multi-step automation pipelines...",
},
{
type: "section",
heading: "Triggers",
snippet: "Configure triggers to start your workflows automatically...",
},
],
},
{
id: "3",
title: "Authentication",
url: "/docs/guides/authentication",
breadcrumbs: ["Guides", "Authentication"],
matches: [
{
type: "content",
snippet:
"Configure authentication for your application using OAuth 2.0, API keys, or JWT.",
},
{
type: "section",
heading: "OAuth 2.0",
snippet: "Configure OAuth 2.0 authentication for your application...",
},
{
type: "section",
heading: "API Keys",
snippet: "Generate and manage API keys from the dashboard...",
},
],
},
{
id: "4",
title: "Database.do",
url: "/docs/apis/database",
breadcrumbs: ["APIs", "Database"],
matches: [
{
type: "content",
snippet: "A fully managed database with automatic backups and scaling.",
},
{
type: "section",
heading: "Queries",
snippet: "Execute SQL queries with Database.do's query builder...",
},
],
},
{
id: "5",
title: "Deployment",
url: "/docs/guides/deployment",
breadcrumbs: ["Guides", "Deployment"],
matches: [
{
type: "section",
heading: "Vercel",
snippet: "Deploy to Vercel with zero configuration...",
},
{
type: "section",
heading: "Docker",
snippet: "Containerize your application with Docker...",
},
],
},
{
id: "6",
title: "LLM.do",
url: "/docs/apis/llm",
breadcrumbs: ["APIs", "LLM"],
matches: [
{
type: "content",
snippet: "Access powerful language models through a simple API.",
},
{
type: "section",
heading: "Chat Completions",
snippet: "Generate chat completions using LLM.do...",
},
{
type: "section",
heading: "Streaming",
snippet: "Stream responses in real-time for better UX...",
},
],
},
];
/**
* Wrapper that renders the trigger button
*/
function TriggerWrapper() {
const { setIsOpen } = useSearchContext();
return (
setIsOpen(true)} />
);
}
/**
* Wrapper that opens the palette automatically
*/
function AutoOpenWrapper({ initialQuery = "" }: { initialQuery?: string }) {
const { setIsOpen, setQuery } = useSearchContext();
useEffect(() => {
setIsOpen(true);
if (initialQuery) {
setQuery(initialQuery);
}
}, [setIsOpen, setQuery, initialQuery]);
return null;
}
/**
* Story wrapper component that combines provider, palette, and optional trigger
*/
function SearchBoxStory({
documents = mockDocuments,
placeholder,
showTrigger = true,
autoOpen = false,
initialQuery = "",
}: {
documents?: SearchResult[];
placeholder?: string;
showTrigger?: boolean;
autoOpen?: boolean;
initialQuery?: string;
}) {
return (
{showTrigger && }
{autoOpen && }
);
}
const meta: Meta = {
title: "Widgets/SearchBox",
component: SearchBoxStory,
parameters: {
layout: "centered",
docs: {
description: {
component: `
Command palette-style search widget for documentation and content search.
Features:
- **Keyboard Shortcut** - Opens with ⌘K / Ctrl+K
- **Fuzzy Search** - Matches across titles, headings, and content
- **Breadcrumbs** - Shows document path for context
- **Highlighted Matches** - Query text highlighted in results
\`\`\`tsx
import { SearchProvider, SearchPalette, SearchTrigger, useSearchContext } from '@mdxui/widgets'
setIsOpen(true)} />
\`\`\`
`,
},
},
},
tags: ["autodocs"],
argTypes: {
documents: { control: false },
placeholder: { control: "text" },
showTrigger: { control: "boolean" },
autoOpen: { control: "boolean" },
initialQuery: { control: "text" },
},
};
export default meta;
type Story = StoryObj;
/**
* Default state showing the search trigger button.
* Click the button or press ⌘K to open the command palette.
*/
export const Default: Story = {
args: {
documents: mockDocuments,
placeholder: "Search documentation...",
showTrigger: true,
autoOpen: false,
},
};
/**
* Command palette open and ready for search.
* Type to search through the documentation.
*/
export const PaletteOpen: Story = {
args: {
documents: mockDocuments,
placeholder: "Search documentation...",
showTrigger: false,
autoOpen: true,
},
};
/**
* Shows search results for the query "functions".
* Results are ranked by relevance with query text highlighted.
*/
export const WithResults: Story = {
args: {
documents: mockDocuments,
placeholder: "Search documentation...",
showTrigger: false,
autoOpen: true,
initialQuery: "functions",
},
};
/**
* Shows the empty state when no results match the query.
*/
export const NoResults: Story = {
args: {
documents: mockDocuments,
placeholder: "Search documentation...",
showTrigger: false,
autoOpen: true,
initialQuery: "xyznonexistent",
},
};
/**
* Shows the palette with no documents provided.
* Useful for testing the empty/loading state.
*/
export const NoDocuments: Story = {
args: {
documents: [],
placeholder: "Search documentation...",
showTrigger: false,
autoOpen: true,
initialQuery: "test",
},
};
/**
* Shows a custom placeholder text in the search input.
*/
export const CustomPlaceholder: Story = {
args: {
documents: mockDocuments,
placeholder: "Search APIs, guides, and more...",
showTrigger: true,
autoOpen: false,
},
};