Skip to content

Commit bd321ae

Browse files
committed
Add Input component
1 parent 460bdea commit bd321ae

File tree

3 files changed

+255
-0
lines changed

3 files changed

+255
-0
lines changed
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
import { Meta, StoryObj } from '@storybook/react-vite'
2+
3+
import { Input } from './index'
4+
5+
type Story = StoryObj<typeof meta>
6+
7+
// More on how to set up stories at: https://storybook.js.org/docs/writing-stories#default-export
8+
const meta: Meta<typeof Input> = {
9+
title: 'Devfive/Input',
10+
component: Input,
11+
decorators: [
12+
(Story) => (
13+
<div style={{ padding: '10px' }}>
14+
<Story />
15+
</div>
16+
),
17+
],
18+
}
19+
20+
export const Default: Story = {
21+
args: {
22+
placeholder: 'Input text',
23+
},
24+
}
25+
26+
export const Error: Story = {
27+
args: {
28+
placeholder: 'Input text',
29+
error: true,
30+
errorMessage: 'Error message',
31+
},
32+
}
33+
34+
export const Disabled: Story = {
35+
args: {
36+
placeholder: 'Input text',
37+
disabled: true,
38+
},
39+
}
40+
41+
// export const WithIcon: Story = {
42+
// args: {
43+
// disabled: true,
44+
// icon: (
45+
// <svg
46+
// className={css({ color: '$text' })}
47+
// fill="none"
48+
// height="24"
49+
// viewBox="0 0 24 24"
50+
// width="24"
51+
// xmlns="http://www.w3.org/2000/svg"
52+
// >
53+
// <path
54+
// clipRule="evenodd"
55+
// d="M16.2635 4.3205C15.8763 3.90288 15.2518 3.89202 14.8523 4.29596L6.92714 12.3101C6.77288 12.4661 6.66766 12.6701 6.6262 12.8938L6.19139 15.2388C6.04942 16.0044 6.67528 16.6795 7.38514 16.5264L9.56701 16.0557C9.74988 16.0163 9.91913 15.9232 10.0562 15.7868L16.6085 9.26287C16.6164 9.25496 16.6242 9.24687 16.6319 9.23862L18.0101 7.75198C18.4063 7.32464 18.4063 6.63179 18.0101 6.20445L16.2635 4.3205ZM15.1465 6.39842L15.5325 6.00805L16.4319 6.97821L16.058 7.38159L15.1465 6.39842ZM13.9617 7.59651L14.8868 8.59436L9.08091 14.3751L7.96212 14.6164L8.17961 13.4435L13.9617 7.59651ZM5.91304 18.0303C5.40878 18.0303 5 18.4712 5 19.0152C5 19.5591 5.40878 20 5.91304 20H18.087C18.5912 20 19 19.5591 19 19.0152C19 18.4712 18.5912 18.0303 18.087 18.0303H5.91304Z"
56+
// fill="currentColor"
57+
// fillRule="evenodd"
58+
// />
59+
// </svg>
60+
// ),
61+
// },
62+
// }
63+
64+
// export const WithForm: Story = {
65+
// args: {
66+
// children: 'Input text',
67+
// type: 'submit',
68+
// },
69+
// decorators: [
70+
// (Story, { args }: { args: Story['args'] }) => {
71+
// const [submitted, setSubmitted] = useState<{ text?: string }>({})
72+
// const [value, setValue] = useState('')
73+
// const [error, setError] = useState('')
74+
75+
// return (
76+
// <>
77+
// <div>{submitted.text}</div>
78+
// <form
79+
// onSubmit={(e) => {
80+
// e.preventDefault()
81+
// const formData = new FormData(e.target as HTMLFormElement)
82+
// const data = Object.fromEntries(formData)
83+
84+
// setSubmitted({
85+
// text: data.text as string,
86+
// })
87+
// }}
88+
// >
89+
// <input
90+
// className={css({
91+
// display: 'block',
92+
// mb: '10px',
93+
// })}
94+
// minLength={3}
95+
// name="text"
96+
// onChange={(e) => {
97+
// setValue(e.target.value)
98+
// setError(
99+
// !/[0-9]/.test(e.target.value) && e.target.value.length >= 3
100+
// ? 'Include one or more numbers.'
101+
// : '',
102+
// )
103+
// }}
104+
// placeholder="Include one or more numbers."
105+
// required
106+
// type="text"
107+
// />
108+
// <Story
109+
// args={{
110+
// ...args,
111+
// disabled: value.length < 3,
112+
// danger: !!error,
113+
// }}
114+
// />
115+
// </form>
116+
// </>
117+
// )
118+
// },
119+
// ],
120+
// }
121+
122+
export default meta

packages/components/src/components/Input/__tests__/index.browser.test.tsx

Whitespace-only changes.
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
'use client'
2+
3+
import {
4+
Box,
5+
Button,
6+
css,
7+
DevupThemeTypography,
8+
Input as DevupInput,
9+
} from '@devup-ui/react'
10+
import { ComponentProps, useState } from 'react'
11+
12+
interface InputProps extends ComponentProps<'input'> {
13+
typography?: keyof DevupThemeTypography
14+
error?: boolean
15+
errorMessage?: string
16+
allowClear?: boolean
17+
}
18+
19+
export function Input({
20+
defaultValue,
21+
value: valueProp,
22+
onChange: onChangeProp,
23+
typography,
24+
error = false,
25+
errorMessage,
26+
allowClear = false,
27+
...props
28+
}: InputProps) {
29+
const [value, setValue] = useState(defaultValue ?? '')
30+
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
31+
setValue(e.target.value)
32+
}
33+
const handleClear = () => {
34+
setValue('')
35+
}
36+
const isClearButtonVisible = value && !props.disabled
37+
38+
return (
39+
<Box pos="relative" w="fit-content">
40+
<DevupInput
41+
_disabled={{
42+
selectors: {
43+
'&::placeholder': {
44+
color: '$inputDisabledText',
45+
},
46+
},
47+
bg: '$inputDisabledBg',
48+
border: '1px solid $border',
49+
color: '$inputDisabledText',
50+
}}
51+
_focus={{
52+
bg: '$primaryBg',
53+
border: '1px solid $primary',
54+
outline: 'none',
55+
}}
56+
_hover={{
57+
border: '1px solid $primary',
58+
}}
59+
bg="$inputBg"
60+
border={error ? '1px solid $error' : '1px solid $border'}
61+
borderRadius="8px"
62+
onChange={onChangeProp ?? handleChange}
63+
pl="12px"
64+
pr={isClearButtonVisible ? '32px' : '12px'}
65+
py="12px"
66+
selectors={{
67+
'&::placeholder': {
68+
color: '$inputPlaceholder',
69+
},
70+
}}
71+
styleOrder={1}
72+
transition="all 0.1s ease-in-out"
73+
typography={typography}
74+
value={valueProp ?? value}
75+
{...props}
76+
/>
77+
{isClearButtonVisible && (
78+
<ClearButton
79+
className={css({
80+
display: ['flex', null, allowClear ? 'flex' : 'none'],
81+
})}
82+
onClick={handleClear}
83+
/>
84+
)}
85+
</Box>
86+
)
87+
}
88+
89+
export function ClearButton(props: ComponentProps<'button'>) {
90+
return (
91+
<Button
92+
alignItems="center"
93+
bg="$negative20"
94+
border="none"
95+
borderRadius="50%"
96+
boxSize="20px"
97+
color="$base"
98+
cursor="pointer"
99+
display="flex"
100+
justifyContent="center"
101+
p="2px"
102+
pos="absolute"
103+
right="12px"
104+
styleOrder={1}
105+
top="50%"
106+
transform="translateY(-50%)"
107+
{...props}
108+
>
109+
<svg
110+
fill="none"
111+
height="24"
112+
viewBox="0 0 24 24"
113+
width="24"
114+
xmlns="http://www.w3.org/2000/svg"
115+
>
116+
<path
117+
d="M18 6L6 18"
118+
stroke="currentColor"
119+
strokeLinecap="round"
120+
strokeLinejoin="round"
121+
strokeWidth="2"
122+
/>
123+
<path
124+
d="M6 6L18 18"
125+
stroke="currentColor"
126+
strokeLinecap="round"
127+
strokeLinejoin="round"
128+
strokeWidth="2"
129+
/>
130+
</svg>
131+
</Button>
132+
)
133+
}

0 commit comments

Comments
 (0)