Skip to content

Commit c6d2d89

Browse files
committed
[CommandPalette] Add Storybook stories
1 parent 62ca1e5 commit c6d2d89

1 file changed

Lines changed: 344 additions & 0 deletions

File tree

Lines changed: 344 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,344 @@
1+
import { useState } from "react";
2+
import type { Meta, StoryObj } from "@storybook/react-vite";
3+
import { action } from "storybook/actions";
4+
import {
5+
Home,
6+
Settings,
7+
Users,
8+
FileText,
9+
Search,
10+
Mail,
11+
Calendar,
12+
BarChart,
13+
} from "lucide-react";
14+
15+
import { CommandPalette } from "./commandPalette";
16+
import { Button } from "../button";
17+
import type { CommandPaletteGroup } from "./types";
18+
19+
const filterGroups = (
20+
groups: CommandPaletteGroup[],
21+
query: string
22+
): CommandPaletteGroup[] =>
23+
groups
24+
.map((group) => ({
25+
...group,
26+
items: group.items.filter((item) =>
27+
item.title.toLowerCase().includes(query.toLowerCase())
28+
),
29+
}))
30+
.filter((group) => group.items.length > 0);
31+
32+
const handleSelect = action("onSelect");
33+
34+
const meta: Meta<typeof CommandPalette> = {
35+
title: "Components/CommandPalette",
36+
component: CommandPalette,
37+
parameters: {
38+
docs: { source: { type: "dynamic" } },
39+
layout: "centered",
40+
},
41+
tags: ["autodocs"],
42+
argTypes: {
43+
show: {
44+
control: "boolean",
45+
description: "Controls whether the command palette is visible.",
46+
},
47+
onShowChange: {
48+
description:
49+
"Callback function called when the visibility state changes.",
50+
},
51+
searchQuery: {
52+
control: "text",
53+
description: "The current search query string.",
54+
},
55+
onSearchQueryChange: {
56+
description: "Callback function called when the search query changes.",
57+
},
58+
groups: {
59+
control: "object",
60+
description:
61+
"Array of groups, each containing a title and an array of items to display.",
62+
},
63+
onSelect: {
64+
description:
65+
"Callback function called when an item is selected from the palette.",
66+
},
67+
},
68+
};
69+
70+
export default meta;
71+
type Story = StoryObj<typeof CommandPalette>;
72+
73+
const navigationItems: CommandPaletteGroup[] = [
74+
{
75+
title: "Navigation",
76+
items: [
77+
{
78+
name: "home",
79+
title: "Home",
80+
description: "Go to homepage",
81+
icon: Home,
82+
},
83+
{
84+
name: "settings",
85+
title: "Settings",
86+
description: "App settings",
87+
icon: Settings,
88+
},
89+
{
90+
name: "users",
91+
title: "Users",
92+
description: "Manage users",
93+
icon: Users,
94+
},
95+
{
96+
name: "documents",
97+
title: "Documents",
98+
description: "View documents",
99+
icon: FileText,
100+
},
101+
],
102+
},
103+
];
104+
105+
const multiGroupItems: CommandPaletteGroup[] = [
106+
{
107+
title: "Pages",
108+
items: [
109+
{
110+
name: "home",
111+
title: "Home",
112+
description: "Go to homepage",
113+
icon: Home,
114+
},
115+
{
116+
name: "reports",
117+
title: "Reports",
118+
description: "View reports",
119+
icon: BarChart,
120+
},
121+
],
122+
},
123+
{
124+
title: "Actions",
125+
items: [
126+
{
127+
name: "search",
128+
title: "Search",
129+
description: "Search everything",
130+
icon: Search,
131+
},
132+
{
133+
name: "send-email",
134+
title: "Send Email",
135+
description: "Compose new email",
136+
icon: Mail,
137+
},
138+
{
139+
name: "calendar",
140+
title: "Open Calendar",
141+
description: "View calendar",
142+
icon: Calendar,
143+
},
144+
],
145+
},
146+
{
147+
title: "Settings",
148+
items: [
149+
{
150+
name: "settings",
151+
title: "Settings",
152+
description: "App settings",
153+
icon: Settings,
154+
},
155+
{
156+
name: "users",
157+
title: "Users",
158+
description: "Manage users",
159+
icon: Users,
160+
},
161+
],
162+
},
163+
];
164+
165+
const itemsWithDisabled: CommandPaletteGroup[] = [
166+
{
167+
title: "Actions",
168+
items: [
169+
{
170+
name: "home",
171+
title: "Home",
172+
description: "Go to homepage",
173+
icon: Home,
174+
},
175+
{
176+
name: "settings",
177+
title: "Settings",
178+
description: "Requires admin access",
179+
icon: Settings,
180+
disabled: true,
181+
},
182+
{
183+
name: "users",
184+
title: "Users",
185+
description: "Manage users",
186+
icon: Users,
187+
},
188+
{
189+
name: "reports",
190+
title: "Reports",
191+
description: "Feature coming soon",
192+
icon: BarChart,
193+
disabled: true,
194+
},
195+
],
196+
},
197+
];
198+
199+
export const Default: Story = {
200+
render: () => {
201+
const [show, setShow] = useState(false);
202+
const [searchQuery, setSearchQuery] = useState("");
203+
204+
return (
205+
<div>
206+
<Button onClick={() => setShow(true)}>Open Command Palette</Button>
207+
<CommandPalette
208+
show={show}
209+
onShowChange={setShow}
210+
searchQuery={searchQuery}
211+
onSearchQueryChange={setSearchQuery}
212+
groups={filterGroups(navigationItems, searchQuery)}
213+
onSelect={handleSelect}
214+
/>
215+
</div>
216+
);
217+
},
218+
};
219+
220+
export const MultipleGroups: Story = {
221+
render: () => {
222+
const [show, setShow] = useState(false);
223+
const [searchQuery, setSearchQuery] = useState("");
224+
225+
return (
226+
<div>
227+
<Button onClick={() => setShow(true)}>Open Multi-Group Palette</Button>
228+
<CommandPalette
229+
show={show}
230+
onShowChange={setShow}
231+
searchQuery={searchQuery}
232+
onSearchQueryChange={setSearchQuery}
233+
groups={filterGroups(multiGroupItems, searchQuery)}
234+
onSelect={handleSelect}
235+
/>
236+
</div>
237+
);
238+
},
239+
};
240+
241+
export const WithDisabledItems: Story = {
242+
render: () => {
243+
const [show, setShow] = useState(false);
244+
const [searchQuery, setSearchQuery] = useState("");
245+
246+
return (
247+
<div>
248+
<Button onClick={() => setShow(true)}>
249+
Open Palette with Disabled Items
250+
</Button>
251+
<CommandPalette
252+
show={show}
253+
onShowChange={setShow}
254+
searchQuery={searchQuery}
255+
onSearchQueryChange={setSearchQuery}
256+
groups={filterGroups(itemsWithDisabled, searchQuery)}
257+
onSelect={handleSelect}
258+
/>
259+
</div>
260+
);
261+
},
262+
};
263+
264+
const hiddenTitleGroups: CommandPaletteGroup[] = [
265+
{
266+
title: "Quick Actions",
267+
hideTitle: true,
268+
items: [
269+
{
270+
name: "home",
271+
title: "Home",
272+
description: "Go to homepage",
273+
icon: Home,
274+
},
275+
{
276+
name: "search",
277+
title: "Search",
278+
description: "Search everything",
279+
icon: Search,
280+
},
281+
{
282+
name: "settings",
283+
title: "Settings",
284+
description: "App settings",
285+
icon: Settings,
286+
},
287+
],
288+
},
289+
];
290+
291+
export const HiddenGroupTitle: Story = {
292+
render: () => {
293+
const [show, setShow] = useState(false);
294+
const [searchQuery, setSearchQuery] = useState("");
295+
296+
return (
297+
<div>
298+
<Button onClick={() => setShow(true)}>
299+
Open Palette (Hidden Title)
300+
</Button>
301+
<CommandPalette
302+
show={show}
303+
onShowChange={setShow}
304+
searchQuery={searchQuery}
305+
onSearchQueryChange={setSearchQuery}
306+
groups={filterGroups(hiddenTitleGroups, searchQuery)}
307+
onSelect={handleSelect}
308+
/>
309+
</div>
310+
);
311+
},
312+
};
313+
314+
export const KeyboardShortcut: Story = {
315+
render: () => {
316+
const [show, setShow] = useState(false);
317+
const [searchQuery, setSearchQuery] = useState("");
318+
319+
return (
320+
<div className="text-center">
321+
<p className="text-sm text-ink-gray-5 mb-4">
322+
Press{" "}
323+
<kbd className="px-1.5 py-0.5 text-xs border rounded bg-surface-gray-2">
324+
Ctrl/Cmd
325+
</kbd>{" "}
326+
+{" "}
327+
<kbd className="px-1.5 py-0.5 text-xs border rounded bg-surface-gray-2">
328+
K
329+
</kbd>{" "}
330+
to open the command palette
331+
</p>
332+
<Button onClick={() => setShow(true)}>Or Click Here</Button>
333+
<CommandPalette
334+
show={show}
335+
onShowChange={setShow}
336+
searchQuery={searchQuery}
337+
onSearchQueryChange={setSearchQuery}
338+
groups={filterGroups(navigationItems, searchQuery)}
339+
onSelect={handleSelect}
340+
/>
341+
</div>
342+
);
343+
},
344+
};

0 commit comments

Comments
 (0)