Skip to content

Commit 0a043eb

Browse files
committed
Add Input component
1 parent 1be7cc9 commit 0a043eb

File tree

5 files changed

+135
-0
lines changed

5 files changed

+135
-0
lines changed

packages/components/src/components/heading.stories.tsx

+2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { Meta, StoryObj } from '@storybook/react';
2+
import { hiveThemeDecorator } from '../../../../.storybook/hive-theme-decorator';
23
import { Heading as _Heading, HeadingProps } from './heading';
34

45
export default {
@@ -23,6 +24,7 @@ export default {
2324
parameters: {
2425
padding: true,
2526
},
27+
decorators: [hiveThemeDecorator],
2628
} satisfies Meta<HeadingProps>;
2729

2830
export const Heading: StoryObj<HeadingProps> = {

packages/components/src/components/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ export { HeroVideo } from './hero-video';
99
export * from './icons';
1010
export { Image } from './image';
1111
export { InfoList } from './info-list';
12+
export { Input } from './input';
1213
export { LegacyPackageCmd } from './legacy-package-cmd';
1314
export { MarketplaceList } from './marketplace-list';
1415
export { MarketplaceSearch } from './marketplace-search';
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
import { Meta, StoryObj } from '@storybook/react';
2+
import { hiveThemeDecorator } from '../../../../.storybook/hive-theme-decorator';
3+
import { Input, InputProps } from './input';
4+
5+
export default {
6+
title: 'Components/Input',
7+
component: Input,
8+
argTypes: {
9+
severity: {
10+
control: 'select',
11+
options: ['critical', 'warning', 'positive', undefined],
12+
},
13+
message: {
14+
control: 'text',
15+
},
16+
type: {
17+
control: 'select',
18+
options: ['text', 'email', 'password', 'number'],
19+
},
20+
disabled: {
21+
control: 'boolean',
22+
},
23+
required: {
24+
control: 'boolean',
25+
},
26+
},
27+
parameters: {
28+
padding: true,
29+
},
30+
decorators: [hiveThemeDecorator],
31+
} satisfies Meta<InputProps>;
32+
33+
export const Default: StoryObj<InputProps> = {
34+
args: {
35+
placeholder: 'Email',
36+
type: 'text',
37+
},
38+
};
39+
40+
export const Critical: StoryObj<InputProps> = {
41+
args: {
42+
severity: 'critical',
43+
message: 'Please enter a valid email address',
44+
type: 'email',
45+
value: '+48 222 500 151',
46+
},
47+
};
48+
49+
export const Warning: StoryObj<InputProps> = {
50+
args: {
51+
severity: 'warning',
52+
message: 'Weak password',
53+
type: 'password',
54+
value: '1234',
55+
},
56+
};
57+
58+
export const Positive: StoryObj<InputProps> = {
59+
args: {
60+
severity: 'positive',
61+
message: 'Very strong password',
62+
type: 'password',
63+
value: 'Wednesday, 2 April 2025, GraphQL will prevail!',
64+
},
65+
};
66+
67+
export const Disabled: StoryObj<InputProps> = {
68+
args: {
69+
disabled: true,
70+
placeholder: 'Disabled input',
71+
},
72+
};
73+
74+
export const Required: StoryObj<InputProps> = {
75+
args: {
76+
required: true,
77+
placeholder: 'This should not be empty',
78+
},
79+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import { cn } from '../cn';
2+
import { Severity } from '../types/severity';
3+
4+
export interface InputProps extends React.InputHTMLAttributes<HTMLInputElement> {
5+
severity?: Severity;
6+
message?: string;
7+
}
8+
9+
export function Input({ severity, message, ...props }: InputProps) {
10+
return (
11+
<div
12+
// todo: discuss colors with designers.
13+
// dark mode colors are kinda bad, but we don't really need
14+
// them just yet as this is used on yellow backgrounds
15+
className={cn(
16+
'rounded-[9px] border border-blue-400 bg-white outline-offset-2 focus-within:outline focus-within:outline-2 dark:border-neutral-400 dark:bg-neutral-800',
17+
'focus-visible:outline-green-800/40',
18+
'[&:focus-within:has([aria-invalid],:invalid)]:outline-critical-dark [&:has([aria-invalid],:invalid)]:border-critical-dark/50',
19+
severity === 'warning' &&
20+
'border-warning-bright/50 outline-warning-bright dark:border-warning-bright/50',
21+
severity === 'positive' &&
22+
'border-positive-dark/50 outline-positive-dark dark:border-positive-dark/50',
23+
)}
24+
>
25+
<input
26+
aria-invalid={severity === 'critical' ? true : undefined}
27+
className={cn(
28+
'w-full rounded-lg bg-white py-3 indent-4 font-medium transition-[background-color] placeholder:text-green-800 placeholder-shown:bg-blue-100 autofill:shadow-[inset_0_0_0px_1000px_rgb(255,255,255)] autofill:[-webkit-text-fill-color:theme(colors.green.1000)] autofill:first-line:font-sans hover:bg-white focus:bg-white focus-visible:outline-none focus-visible:ring-0 dark:bg-neutral-800 dark:text-white dark:placeholder:text-neutral-300 dark:placeholder-shown:bg-neutral-900 dark:hover:bg-neutral-800 dark:focus:bg-neutral-800',
29+
message && 'rounded-b-none',
30+
'pr-6 [&:is(:invalid,[aria-invalid])]:border-critical-dark/20 [&:is(:invalid,[aria-invalid])]:placeholder-shown:bg-critical-dark/5',
31+
32+
props.className,
33+
)}
34+
{...props}
35+
/>
36+
{message &&
37+
(severity === 'critical' ? (
38+
<p className="rounded-b-lg bg-critical-dark/10 py-0.5 pl-4 text-sm text-critical-dark dark:bg-critical-bright/20 dark:text-white">
39+
{message}
40+
</p>
41+
) : severity === 'warning' ? (
42+
<p className="rounded-b-lg bg-warning-bright/10 py-0.5 pl-4 text-sm text-warning-bright">
43+
{message}
44+
</p>
45+
) : (
46+
<p className="rounded-b-lg bg-positive-dark/10 py-0.5 pl-4 text-sm text-positive-dark">
47+
{message}
48+
</p>
49+
))}
50+
</div>
51+
);
52+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export type Severity = 'critical' | 'positive' | 'warning';

0 commit comments

Comments
 (0)