Skip to content

Commit 61ca21a

Browse files
committed
chore: add stories to propel
1 parent a69c6f1 commit 61ca21a

36 files changed

Lines changed: 4405 additions & 807 deletions

packages/propel/.storybook/main.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ function getAbsolutePath(value: string) {
1111
}
1212
const config: StorybookConfig = {
1313
stories: ["../src/**/*.stories.@(ts|tsx)"],
14-
addons: ["@storybook/addon-designs", "@storybook/addon-docs"],
14+
addons: [getAbsolutePath("@storybook/addon-designs"), getAbsolutePath("@storybook/addon-docs")],
1515
framework: {
1616
name: getAbsolutePath("@storybook/react-vite"),
1717
options: {},

packages/propel/package.json

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -55,8 +55,8 @@
5555
"@plane/types": "workspace:*",
5656
"@tanstack/react-table": "^8.21.3",
5757
"class-variance-authority": "^0.7.1",
58-
"cmdk": "^1.1.1",
5958
"clsx": "^2.1.1",
59+
"cmdk": "^1.1.1",
6060
"frimousse": "^0.3.0",
6161
"lucide-react": "catalog:",
6262
"react": "catalog:",
@@ -71,12 +71,12 @@
7171
"@plane/tailwind-config": "workspace:*",
7272
"@plane/typescript-config": "workspace:*",
7373
"@storybook/addon-designs": "10.0.2",
74-
"@storybook/addon-docs": "9.1.2",
75-
"@storybook/react-vite": "9.1.2",
74+
"@storybook/addon-docs": "9.1.10",
75+
"@storybook/react-vite": "9.1.10",
7676
"@types/react": "catalog:",
7777
"@types/react-dom": "catalog:",
78-
"eslint-plugin-storybook": "9.1.2",
79-
"storybook": "9.1.2",
78+
"eslint-plugin-storybook": "9.1.10",
79+
"storybook": "9.1.10",
8080
"tsdown": "catalog:",
8181
"typescript": "catalog:"
8282
}
Lines changed: 198 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,198 @@
1+
import type { Meta, StoryObj } from "@storybook/react-vite";
2+
import { Accordion } from "./accordion";
3+
4+
const meta = {
5+
title: "Components/Accordion",
6+
component: Accordion.Root,
7+
parameters: {
8+
layout: "centered",
9+
controls: { disable: true },
10+
},
11+
tags: ["autodocs"],
12+
subcomponents: {
13+
Item: Accordion.Item,
14+
Trigger: Accordion.Trigger,
15+
Content: Accordion.Content,
16+
},
17+
args: {
18+
children: null,
19+
},
20+
} satisfies Meta<typeof Accordion.Root>;
21+
22+
export default meta;
23+
type Story = StoryObj<typeof meta>;
24+
25+
export const Default: Story = {
26+
render() {
27+
return (
28+
<Accordion.Root className="w-96">
29+
<Accordion.Item value="item-1">
30+
<Accordion.Trigger>What is Plane?</Accordion.Trigger>
31+
<Accordion.Content>
32+
Plane is an open-source project management tool designed for developers and teams to plan, track, and manage
33+
their work efficiently.
34+
</Accordion.Content>
35+
</Accordion.Item>
36+
<Accordion.Item value="item-2">
37+
<Accordion.Trigger>How do I get started?</Accordion.Trigger>
38+
<Accordion.Content>
39+
You can get started by signing up for an account, creating your first workspace, and inviting your team
40+
members to collaborate.
41+
</Accordion.Content>
42+
</Accordion.Item>
43+
<Accordion.Item value="item-3">
44+
<Accordion.Trigger>Is it free to use?</Accordion.Trigger>
45+
<Accordion.Content>
46+
Plane offers both free and paid plans. The free plan includes essential features for small teams, while paid
47+
plans unlock advanced functionality.
48+
</Accordion.Content>
49+
</Accordion.Item>
50+
</Accordion.Root>
51+
);
52+
},
53+
};
54+
55+
export const SingleOpen: Story = {
56+
render() {
57+
return (
58+
<Accordion.Root defaultValue={["item-1"]} className="w-96">
59+
<Accordion.Item value="item-1">
60+
<Accordion.Trigger>Section 1</Accordion.Trigger>
61+
<Accordion.Content>Content for section 1. Only one section can be open at a time.</Accordion.Content>
62+
</Accordion.Item>
63+
<Accordion.Item value="item-2">
64+
<Accordion.Trigger>Section 2</Accordion.Trigger>
65+
<Accordion.Content>Content for section 2.</Accordion.Content>
66+
</Accordion.Item>
67+
<Accordion.Item value="item-3">
68+
<Accordion.Trigger>Section 3</Accordion.Trigger>
69+
<Accordion.Content>Content for section 3.</Accordion.Content>
70+
</Accordion.Item>
71+
</Accordion.Root>
72+
);
73+
},
74+
};
75+
76+
export const AllowMultiple: Story = {
77+
render() {
78+
return (
79+
<Accordion.Root allowMultiple defaultValue={["item-1", "item-2"]} className="w-96">
80+
<Accordion.Item value="item-1">
81+
<Accordion.Trigger>First Section</Accordion.Trigger>
82+
<Accordion.Content>Multiple sections can be open at the same time.</Accordion.Content>
83+
</Accordion.Item>
84+
<Accordion.Item value="item-2">
85+
<Accordion.Trigger>Second Section</Accordion.Trigger>
86+
<Accordion.Content>This section is also open by default.</Accordion.Content>
87+
</Accordion.Item>
88+
<Accordion.Item value="item-3">
89+
<Accordion.Trigger>Third Section</Accordion.Trigger>
90+
<Accordion.Content>You can open this section while keeping the others open.</Accordion.Content>
91+
</Accordion.Item>
92+
</Accordion.Root>
93+
);
94+
},
95+
};
96+
97+
export const WithDisabledItem: Story = {
98+
render() {
99+
return (
100+
<Accordion.Root className="w-96">
101+
<Accordion.Item value="item-1">
102+
<Accordion.Trigger>Enabled Section</Accordion.Trigger>
103+
<Accordion.Content>This section can be toggled.</Accordion.Content>
104+
</Accordion.Item>
105+
<Accordion.Item value="item-2" disabled>
106+
<Accordion.Trigger>Disabled Section</Accordion.Trigger>
107+
<Accordion.Content>This content cannot be accessed.</Accordion.Content>
108+
</Accordion.Item>
109+
<Accordion.Item value="item-3">
110+
<Accordion.Trigger>Another Enabled Section</Accordion.Trigger>
111+
<Accordion.Content>This section can also be toggled.</Accordion.Content>
112+
</Accordion.Item>
113+
</Accordion.Root>
114+
);
115+
},
116+
};
117+
118+
export const CustomIcon: Story = {
119+
render() {
120+
return (
121+
<Accordion.Root className="w-96">
122+
<Accordion.Item value="item-1">
123+
<Accordion.Trigger
124+
icon={
125+
<svg
126+
width="16"
127+
height="16"
128+
viewBox="0 0 24 24"
129+
fill="none"
130+
stroke="currentColor"
131+
strokeWidth="2"
132+
strokeLinecap="round"
133+
strokeLinejoin="round"
134+
className="transition-transform group-data-[panel-open]:rotate-180"
135+
>
136+
<path d="m6 9 6 6 6-6" />
137+
</svg>
138+
}
139+
>
140+
Custom Chevron Icon
141+
</Accordion.Trigger>
142+
<Accordion.Content>
143+
This accordion uses a custom chevron icon instead of the default plus icon.
144+
</Accordion.Content>
145+
</Accordion.Item>
146+
<Accordion.Item value="item-2">
147+
<Accordion.Trigger
148+
icon={
149+
<svg
150+
width="16"
151+
height="16"
152+
viewBox="0 0 24 24"
153+
fill="none"
154+
stroke="currentColor"
155+
strokeWidth="2"
156+
strokeLinecap="round"
157+
strokeLinejoin="round"
158+
className="transition-transform group-data-[panel-open]:rotate-180"
159+
>
160+
<path d="m6 9 6 6 6-6" />
161+
</svg>
162+
}
163+
>
164+
Another Section
165+
</Accordion.Trigger>
166+
<Accordion.Content>All items in this accordion use the custom icon.</Accordion.Content>
167+
</Accordion.Item>
168+
</Accordion.Root>
169+
);
170+
},
171+
};
172+
173+
export const AsChildTrigger: Story = {
174+
render() {
175+
return (
176+
<Accordion.Root className="w-96">
177+
<Accordion.Item value="item-1">
178+
<Accordion.Trigger asChild>
179+
<button className="w-full rounded-md bg-blue-500 px-4 py-2 text-left text-white hover:bg-blue-600">
180+
Custom Button Trigger
181+
</button>
182+
</Accordion.Trigger>
183+
<Accordion.Content>
184+
When using asChild, you can completely customize the trigger element without the default icon wrapper.
185+
</Accordion.Content>
186+
</Accordion.Item>
187+
<Accordion.Item value="item-2">
188+
<Accordion.Trigger asChild>
189+
<button className="w-full rounded-md bg-green-500 px-4 py-2 text-left text-white hover:bg-green-600">
190+
Another Custom Trigger
191+
</button>
192+
</Accordion.Trigger>
193+
<Accordion.Content>This gives you full control over the trigger styling and behavior.</Accordion.Content>
194+
</Accordion.Item>
195+
</Accordion.Root>
196+
);
197+
},
198+
};

packages/propel/src/accordion/accordion.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,29 +2,29 @@ import * as React from "react";
22
import { Accordion as BaseAccordion } from "@base-ui-components/react";
33
import { PlusIcon } from "lucide-react";
44

5-
interface AccordionRootProps {
5+
export interface AccordionRootProps {
66
defaultValue?: string[];
77
allowMultiple?: boolean;
88
className?: string;
99
children: React.ReactNode;
1010
}
1111

12-
interface AccordionItemProps {
12+
export interface AccordionItemProps {
1313
value: string;
1414
disabled?: boolean;
1515
className?: string;
1616
children: React.ReactNode;
1717
}
1818

19-
interface AccordionTriggerProps {
19+
export interface AccordionTriggerProps {
2020
className?: string;
2121
icon?: React.ReactNode;
2222
children: React.ReactNode;
2323
asChild?: boolean;
2424
iconClassName?: string;
2525
}
2626

27-
interface AccordionContentProps {
27+
export interface AccordionContentProps {
2828
className?: string;
2929
contentWrapperClassName?: string;
3030
children: React.ReactNode;

packages/propel/src/animated-counter/animated-counter.stories.tsx

Lines changed: 31 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -2,54 +2,45 @@ import { useState } from "react";
22
import type { Meta, StoryObj } from "@storybook/react-vite";
33
import { AnimatedCounter } from "./animated-counter";
44

5-
const meta: Meta<typeof AnimatedCounter> = {
5+
const meta = {
66
title: "Components/AnimatedCounter",
77
component: AnimatedCounter,
88
parameters: {
99
layout: "centered",
1010
},
1111
tags: ["autodocs"],
12-
argTypes: {
13-
size: {
14-
control: { type: "select" },
15-
options: ["sm", "md", "lg"],
16-
},
12+
args: {
13+
size: "md",
14+
count: 0,
1715
},
18-
};
19-
20-
export default meta;
21-
type Story = StoryObj<typeof AnimatedCounter>;
22-
23-
const AnimatedCounterDemo = (args: React.ComponentProps<typeof AnimatedCounter>) => {
24-
const [count, setCount] = useState(args.count || 0);
16+
render(args) {
17+
const [count, setCount] = useState(args.count);
2518

26-
return (
27-
<div className="space-y-6 p-4">
28-
<div className="flex items-center justify-center gap-6">
29-
<button
30-
className="px-4 py-2 bg-red-500 text-white font-medium rounded-lg hover:bg-red-600 focus:outline-none focus:ring-2 focus:ring-red-400 focus:ring-offset-2 transition-colors shadow-md"
31-
onClick={() => setCount((prev) => Math.max(0, prev - 1))}
32-
>
33-
-1
34-
</button>
35-
<div className="flex items-center justify-center min-w-[60px] h-12 bg-gray-50 border border-gray-200 rounded-lg">
36-
<AnimatedCounter {...args} count={count} />
19+
return (
20+
<div className="space-y-6 p-4">
21+
<div className="flex items-center justify-center gap-6">
22+
<button
23+
className="px-4 py-2 bg-red-500 text-white font-medium rounded-lg hover:bg-red-600 focus:outline-none focus:ring-2 focus:ring-red-400 focus:ring-offset-2 transition-colors shadow-md"
24+
onClick={() => setCount((prev) => Math.max(0, prev - 1))}
25+
>
26+
-1
27+
</button>
28+
<div className="flex items-center justify-center min-w-[60px] h-12 bg-gray-50 border border-gray-200 rounded-lg">
29+
<AnimatedCounter {...args} count={count} />
30+
</div>
31+
<button
32+
className="px-4 py-2 bg-green-500 text-white font-medium rounded-lg hover:bg-green-600 focus:outline-none focus:ring-2 focus:ring-green-400 focus:ring-offset-2 transition-colors shadow-md"
33+
onClick={() => setCount((prev) => prev + 1)}
34+
>
35+
+1
36+
</button>
3737
</div>
38-
<button
39-
className="px-4 py-2 bg-green-500 text-white font-medium rounded-lg hover:bg-green-600 focus:outline-none focus:ring-2 focus:ring-green-400 focus:ring-offset-2 transition-colors shadow-md"
40-
onClick={() => setCount((prev) => prev + 1)}
41-
>
42-
+1
43-
</button>
4438
</div>
45-
</div>
46-
);
47-
};
48-
49-
export const Default: Story = {
50-
render: (args) => <AnimatedCounterDemo {...args} />,
51-
args: {
52-
count: 5,
53-
size: "md",
39+
);
5440
},
55-
};
41+
} satisfies Meta<typeof AnimatedCounter>;
42+
43+
export default meta;
44+
type Story = StoryObj<typeof meta>;
45+
46+
export const Default: Story = {};

0 commit comments

Comments
 (0)