Skip to content

Commit 48bb41d

Browse files
committed
feat: add Input and Select stories, tests, and library integration
1 parent 58fa6cf commit 48bb41d

File tree

11 files changed

+2126
-2
lines changed

11 files changed

+2126
-2
lines changed

apps/docs/.storybook/main.cjs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,20 @@ const config = {
4141
"../../../packages/react/src/components/button/index.ts",
4242
),
4343
},
44+
{
45+
find: "@prism/react/input",
46+
replacement: resolve(
47+
__dirname,
48+
"../../../packages/react/src/components/input/index.ts",
49+
),
50+
},
51+
{
52+
find: "@prism/react/select",
53+
replacement: resolve(
54+
__dirname,
55+
"../../../packages/react/src/components/select/index.ts",
56+
),
57+
},
4458
{
4559
find: "@prism/react",
4660
replacement: resolve(__dirname, "../../../packages/react/src"),
Lines changed: 326 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,326 @@
1+
import type { Meta, StoryObj } from "@storybook/react";
2+
3+
import { Input } from "@prism/react/input";
4+
5+
/**
6+
* Input Component Stories
7+
*
8+
* Showcases different input types, states, and usage patterns
9+
* following the shadcn/ui implementation.
10+
*/
11+
12+
const meta: Meta<typeof Input> = {
13+
title: "Components/Input",
14+
component: Input,
15+
parameters: {
16+
layout: "centered",
17+
},
18+
tags: ["autodocs"],
19+
argTypes: {
20+
type: {
21+
control: "select",
22+
options: [
23+
"text",
24+
"email",
25+
"password",
26+
"number",
27+
"tel",
28+
"url",
29+
"search",
30+
"date",
31+
"time",
32+
"file",
33+
],
34+
description: "The type of input field",
35+
},
36+
placeholder: {
37+
control: "text",
38+
description: "Placeholder text",
39+
},
40+
disabled: {
41+
control: "boolean",
42+
description: "Whether the input is disabled",
43+
},
44+
required: {
45+
control: "boolean",
46+
description: "Whether the input is required",
47+
},
48+
},
49+
};
50+
51+
export default meta;
52+
type Story = StoryObj<typeof Input>;
53+
54+
/**
55+
* Default Input
56+
*/
57+
export const Default: Story = {
58+
args: {
59+
placeholder: "Type something...",
60+
},
61+
};
62+
63+
/**
64+
* Email Input
65+
*
66+
* Input configured for email addresses
67+
*/
68+
export const Email: Story = {
69+
args: {
70+
type: "email",
71+
placeholder: "Email",
72+
},
73+
};
74+
75+
/**
76+
* Password Input
77+
*
78+
* Input with obscured text for passwords
79+
*/
80+
export const Password: Story = {
81+
args: {
82+
type: "password",
83+
placeholder: "Password",
84+
},
85+
};
86+
87+
/**
88+
* Number Input
89+
*
90+
* Input for numeric values with spinner controls
91+
*/
92+
export const Number: Story = {
93+
args: {
94+
type: "number",
95+
placeholder: "Enter a number",
96+
},
97+
};
98+
99+
/**
100+
* Search Input
101+
*
102+
* Input optimized for search functionality
103+
*/
104+
export const Search: Story = {
105+
args: {
106+
type: "search",
107+
placeholder: "Search...",
108+
},
109+
};
110+
111+
/**
112+
* Disabled State
113+
*
114+
* Input that cannot be interacted with
115+
*/
116+
export const Disabled: Story = {
117+
args: {
118+
disabled: true,
119+
placeholder: "Disabled input",
120+
},
121+
};
122+
123+
/**
124+
* With Default Value
125+
*
126+
* Input with a pre-filled value
127+
*/
128+
export const WithDefaultValue: Story = {
129+
args: {
130+
defaultValue: "Pre-filled value",
131+
},
132+
};
133+
134+
/**
135+
* File Input
136+
*
137+
* Input for file uploads
138+
*/
139+
export const File: Story = {
140+
render: () => (
141+
<div style={{ display: "grid", gap: "0.75rem", maxWidth: "20rem" }}>
142+
<label
143+
htmlFor="file-input"
144+
style={{ fontSize: "0.875rem", fontWeight: 500 }}
145+
>
146+
Upload file
147+
</label>
148+
<Input id="file-input" type="file" />
149+
</div>
150+
),
151+
};
152+
153+
/**
154+
* With Label
155+
*
156+
* Input paired with a label for accessibility
157+
*/
158+
export const WithLabel: Story = {
159+
render: () => (
160+
<div style={{ display: "grid", gap: "0.75rem", maxWidth: "20rem" }}>
161+
<label
162+
htmlFor="email-input"
163+
style={{ fontSize: "0.875rem", fontWeight: 500 }}
164+
>
165+
Email
166+
</label>
167+
<Input id="email-input" placeholder="m@example.com" type="email" />
168+
</div>
169+
),
170+
};
171+
172+
/**
173+
* With Button
174+
*
175+
* Input combined with a button (subscription pattern)
176+
*/
177+
export const WithButton: Story = {
178+
render: () => (
179+
<div style={{ display: "flex", gap: "0.5rem", maxWidth: "24rem" }}>
180+
<Input placeholder="Email" style={{ flex: 1 }} type="email" />
181+
<button
182+
style={{
183+
padding: "0 1rem",
184+
backgroundColor: "#0ea5e9",
185+
color: "white",
186+
borderRadius: "0.375rem",
187+
border: "none",
188+
cursor: "pointer",
189+
fontWeight: 500,
190+
}}
191+
type="button"
192+
>
193+
Subscribe
194+
</button>
195+
</div>
196+
),
197+
};
198+
199+
/**
200+
* Form Example
201+
*
202+
* Input used in a complete form with labels and helper text
203+
*/
204+
export const FormExample: Story = {
205+
render: () => (
206+
<form
207+
onSubmit={(e) => {
208+
e.preventDefault();
209+
// eslint-disable-next-line no-alert -- Storybook demo needs alert for user feedback
210+
alert("Form submitted!");
211+
}}
212+
style={{ display: "grid", gap: "1rem", maxWidth: "20rem" }}
213+
>
214+
<div style={{ display: "grid", gap: "0.5rem" }}>
215+
<label htmlFor="name" style={{ fontSize: "0.875rem", fontWeight: 500 }}>
216+
Name
217+
</label>
218+
<Input id="name" placeholder="John Doe" required />
219+
</div>
220+
221+
<div style={{ display: "grid", gap: "0.5rem" }}>
222+
<label
223+
htmlFor="email-form"
224+
style={{ fontSize: "0.875rem", fontWeight: 500 }}
225+
>
226+
Email
227+
</label>
228+
<Input
229+
id="email-form"
230+
placeholder="m@example.com"
231+
required
232+
type="email"
233+
/>
234+
<p style={{ fontSize: "0.75rem", color: "#737373" }}>
235+
We&apos;ll never share your email.
236+
</p>
237+
</div>
238+
239+
<button
240+
style={{
241+
padding: "0.5rem 1rem",
242+
backgroundColor: "#0ea5e9",
243+
color: "white",
244+
borderRadius: "0.375rem",
245+
border: "none",
246+
cursor: "pointer",
247+
fontWeight: 500,
248+
}}
249+
type="submit"
250+
>
251+
Submit
252+
</button>
253+
</form>
254+
),
255+
};
256+
257+
/**
258+
* Different Input Types
259+
*
260+
* Showcase of various HTML5 input types
261+
*/
262+
export const InputTypes: Story = {
263+
render: () => (
264+
<div style={{ display: "grid", gap: "1rem", maxWidth: "20rem" }}>
265+
<div style={{ display: "grid", gap: "0.5rem" }}>
266+
<label
267+
htmlFor="input-text"
268+
style={{ fontSize: "0.875rem", fontWeight: 500 }}
269+
>
270+
Text
271+
</label>
272+
<Input id="input-text" placeholder="Text input" type="text" />
273+
</div>
274+
275+
<div style={{ display: "grid", gap: "0.5rem" }}>
276+
<label
277+
htmlFor="input-email"
278+
style={{ fontSize: "0.875rem", fontWeight: 500 }}
279+
>
280+
Email
281+
</label>
282+
<Input id="input-email" placeholder="email@example.com" type="email" />
283+
</div>
284+
285+
<div style={{ display: "grid", gap: "0.5rem" }}>
286+
<label
287+
htmlFor="input-password"
288+
style={{ fontSize: "0.875rem", fontWeight: 500 }}
289+
>
290+
Password
291+
</label>
292+
<Input id="input-password" placeholder="••••••••" type="password" />
293+
</div>
294+
295+
<div style={{ display: "grid", gap: "0.5rem" }}>
296+
<label
297+
htmlFor="input-number"
298+
style={{ fontSize: "0.875rem", fontWeight: 500 }}
299+
>
300+
Number
301+
</label>
302+
<Input id="input-number" placeholder="123" type="number" />
303+
</div>
304+
305+
<div style={{ display: "grid", gap: "0.5rem" }}>
306+
<label
307+
htmlFor="input-date"
308+
style={{ fontSize: "0.875rem", fontWeight: 500 }}
309+
>
310+
Date
311+
</label>
312+
<Input id="input-date" type="date" />
313+
</div>
314+
315+
<div style={{ display: "grid", gap: "0.5rem" }}>
316+
<label
317+
htmlFor="input-time"
318+
style={{ fontSize: "0.875rem", fontWeight: 500 }}
319+
>
320+
Time
321+
</label>
322+
<Input id="input-time" type="time" />
323+
</div>
324+
</div>
325+
),
326+
};

0 commit comments

Comments
 (0)