@capyx/components-library 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.storybook/main.ts +33 -0
- package/.storybook/preview.ts +36 -0
- package/.storybook/vitest.setup.ts +7 -0
- package/README.md +210 -0
- package/biome.json +37 -0
- package/lib/addons/CharacterCountInput.tsx +204 -0
- package/lib/addons/index.ts +2 -0
- package/lib/components/CheckInput.tsx +126 -0
- package/lib/components/DateInput.tsx +179 -0
- package/lib/components/FileInput.tsx +353 -0
- package/lib/components/RichTextInput.tsx +112 -0
- package/lib/components/SelectInput.tsx +144 -0
- package/lib/components/SwitchInput.tsx +116 -0
- package/lib/components/TagsInput.tsx +118 -0
- package/lib/components/TextAreaInput.tsx +211 -0
- package/lib/components/TextInput.tsx +381 -0
- package/lib/components/index.ts +9 -0
- package/lib/index.ts +2 -0
- package/package.json +72 -0
- package/stories/CharacterCountInput.stories.tsx +104 -0
- package/stories/CheckInput.stories.tsx +80 -0
- package/stories/DateInput.stories.tsx +137 -0
- package/stories/FileInput.stories.tsx +125 -0
- package/stories/RichTextInput.stories.tsx +77 -0
- package/stories/SelectInput.stories.tsx +131 -0
- package/stories/SwitchInput.stories.tsx +80 -0
- package/stories/TagsInput.stories.tsx +69 -0
- package/stories/TextAreaInput.stories.tsx +117 -0
- package/stories/TextInput.stories.tsx +167 -0
- package/vitest.config.ts +37 -0
- package/vitest.shims.d.ts +1 -0
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import { FormProvider, useForm } from 'react-hook-form';
|
|
4
|
+
import { TextAreaInput } from '../lib/components/TextAreaInput';
|
|
5
|
+
|
|
6
|
+
const meta = {
|
|
7
|
+
title: 'Components/TextAreaInput',
|
|
8
|
+
component: TextAreaInput,
|
|
9
|
+
parameters: {
|
|
10
|
+
layout: 'centered',
|
|
11
|
+
},
|
|
12
|
+
tags: ['autodocs'],
|
|
13
|
+
argTypes: {
|
|
14
|
+
controlSize: {
|
|
15
|
+
control: 'select',
|
|
16
|
+
options: ['sm', 'lg', undefined],
|
|
17
|
+
},
|
|
18
|
+
},
|
|
19
|
+
} satisfies Meta<typeof TextAreaInput>;
|
|
20
|
+
|
|
21
|
+
export default meta;
|
|
22
|
+
type Story = StoryObj<typeof meta>;
|
|
23
|
+
|
|
24
|
+
const FormWrapper = ({ children }: { children: React.ReactNode }) => {
|
|
25
|
+
const methods = useForm({
|
|
26
|
+
defaultValues: {
|
|
27
|
+
bio: '',
|
|
28
|
+
description: '',
|
|
29
|
+
comments: '',
|
|
30
|
+
},
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
return <FormProvider {...methods}>{children}</FormProvider>;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
export const Basic: Story = {
|
|
37
|
+
args: {
|
|
38
|
+
name: 'bio',
|
|
39
|
+
label: 'Biography',
|
|
40
|
+
placeholder: 'Tell us about yourself...',
|
|
41
|
+
},
|
|
42
|
+
render: (args) => (
|
|
43
|
+
<FormWrapper>
|
|
44
|
+
<TextAreaInput {...args} />
|
|
45
|
+
</FormWrapper>
|
|
46
|
+
),
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
export const Required: Story = {
|
|
50
|
+
args: {
|
|
51
|
+
name: 'description',
|
|
52
|
+
label: 'Description',
|
|
53
|
+
placeholder: 'Enter description...',
|
|
54
|
+
required: true,
|
|
55
|
+
},
|
|
56
|
+
render: (args) => (
|
|
57
|
+
<FormWrapper>
|
|
58
|
+
<TextAreaInput {...args} />
|
|
59
|
+
</FormWrapper>
|
|
60
|
+
),
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
export const WithMaxLength: Story = {
|
|
64
|
+
args: {
|
|
65
|
+
name: 'bio',
|
|
66
|
+
label: 'Biography',
|
|
67
|
+
maxLength: 500,
|
|
68
|
+
placeholder: 'Max 500 characters',
|
|
69
|
+
},
|
|
70
|
+
render: (args) => (
|
|
71
|
+
<FormWrapper>
|
|
72
|
+
<TextAreaInput {...args} />
|
|
73
|
+
</FormWrapper>
|
|
74
|
+
),
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
export const WithDebounce: Story = {
|
|
78
|
+
args: {
|
|
79
|
+
name: 'comments',
|
|
80
|
+
label: 'Comments',
|
|
81
|
+
placeholder: 'Type with 500ms debounce...',
|
|
82
|
+
debounceMs: 500,
|
|
83
|
+
},
|
|
84
|
+
render: (args) => (
|
|
85
|
+
<FormWrapper>
|
|
86
|
+
<TextAreaInput {...args} />
|
|
87
|
+
</FormWrapper>
|
|
88
|
+
),
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
export const Disabled: Story = {
|
|
92
|
+
args: {
|
|
93
|
+
name: 'bio',
|
|
94
|
+
label: 'Biography',
|
|
95
|
+
placeholder: 'This field is disabled',
|
|
96
|
+
disabled: true,
|
|
97
|
+
},
|
|
98
|
+
render: (args) => (
|
|
99
|
+
<FormWrapper>
|
|
100
|
+
<TextAreaInput {...args} />
|
|
101
|
+
</FormWrapper>
|
|
102
|
+
),
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
export const ReadOnly: Story = {
|
|
106
|
+
args: {
|
|
107
|
+
name: 'bio',
|
|
108
|
+
label: 'Biography',
|
|
109
|
+
value: 'This is a read-only biography text.',
|
|
110
|
+
isReadOnly: true,
|
|
111
|
+
},
|
|
112
|
+
render: (args) => (
|
|
113
|
+
<FormWrapper>
|
|
114
|
+
<TextAreaInput {...args} />
|
|
115
|
+
</FormWrapper>
|
|
116
|
+
),
|
|
117
|
+
};
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import { FormProvider, useForm } from 'react-hook-form';
|
|
4
|
+
import { TextInput } from '../lib/components/TextInput';
|
|
5
|
+
|
|
6
|
+
const meta = {
|
|
7
|
+
title: 'Components/TextInput',
|
|
8
|
+
component: TextInput,
|
|
9
|
+
parameters: {
|
|
10
|
+
layout: 'centered',
|
|
11
|
+
},
|
|
12
|
+
tags: ['autodocs'],
|
|
13
|
+
argTypes: {
|
|
14
|
+
type: {
|
|
15
|
+
control: 'select',
|
|
16
|
+
options: ['text', 'email', 'password', 'number', 'tel', 'url'],
|
|
17
|
+
},
|
|
18
|
+
controlSize: {
|
|
19
|
+
control: 'select',
|
|
20
|
+
options: ['sm', 'lg', undefined],
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
} satisfies Meta<typeof TextInput>;
|
|
24
|
+
|
|
25
|
+
export default meta;
|
|
26
|
+
type Story = StoryObj<typeof meta>;
|
|
27
|
+
|
|
28
|
+
// Wrapper component for react-hook-form integration
|
|
29
|
+
const FormWrapper = ({ children }: { children: React.ReactNode }) => {
|
|
30
|
+
const methods = useForm({
|
|
31
|
+
defaultValues: {
|
|
32
|
+
username: '',
|
|
33
|
+
email: '',
|
|
34
|
+
search: '',
|
|
35
|
+
skill: '',
|
|
36
|
+
},
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
return <FormProvider {...methods}>{children}</FormProvider>;
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
export const Basic: Story = {
|
|
43
|
+
args: {
|
|
44
|
+
name: 'username',
|
|
45
|
+
label: 'Username',
|
|
46
|
+
placeholder: 'Enter your username',
|
|
47
|
+
},
|
|
48
|
+
render: (args) => (
|
|
49
|
+
<FormWrapper>
|
|
50
|
+
<TextInput {...args} />
|
|
51
|
+
</FormWrapper>
|
|
52
|
+
),
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
export const Required: Story = {
|
|
56
|
+
args: {
|
|
57
|
+
name: 'email',
|
|
58
|
+
label: 'Email',
|
|
59
|
+
type: 'email',
|
|
60
|
+
placeholder: 'your.email@example.com',
|
|
61
|
+
required: true,
|
|
62
|
+
},
|
|
63
|
+
render: (args) => (
|
|
64
|
+
<FormWrapper>
|
|
65
|
+
<TextInput {...args} />
|
|
66
|
+
</FormWrapper>
|
|
67
|
+
),
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
export const WithMaxLength: Story = {
|
|
71
|
+
args: {
|
|
72
|
+
name: 'username',
|
|
73
|
+
label: 'Username',
|
|
74
|
+
maxLength: 20,
|
|
75
|
+
placeholder: 'Max 20 characters',
|
|
76
|
+
},
|
|
77
|
+
render: (args) => (
|
|
78
|
+
<FormWrapper>
|
|
79
|
+
<TextInput {...args} />
|
|
80
|
+
</FormWrapper>
|
|
81
|
+
),
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
export const WithAutocomplete: Story = {
|
|
85
|
+
args: {
|
|
86
|
+
name: 'skill',
|
|
87
|
+
label: 'Primary Skill',
|
|
88
|
+
shouldAutoComplete: true,
|
|
89
|
+
values: ['JavaScript', 'TypeScript', 'React', 'Node.js', 'Python', 'Java'],
|
|
90
|
+
placeholder: 'Type to search...',
|
|
91
|
+
},
|
|
92
|
+
render: (args) => (
|
|
93
|
+
<FormWrapper>
|
|
94
|
+
<TextInput {...args} />
|
|
95
|
+
</FormWrapper>
|
|
96
|
+
),
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
export const WithDebounce: Story = {
|
|
100
|
+
args: {
|
|
101
|
+
name: 'search',
|
|
102
|
+
label: 'Search',
|
|
103
|
+
placeholder: 'Search with 300ms debounce...',
|
|
104
|
+
debounceMs: 300,
|
|
105
|
+
},
|
|
106
|
+
render: (args) => (
|
|
107
|
+
<FormWrapper>
|
|
108
|
+
<TextInput {...args} />
|
|
109
|
+
</FormWrapper>
|
|
110
|
+
),
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
export const Disabled: Story = {
|
|
114
|
+
args: {
|
|
115
|
+
name: 'username',
|
|
116
|
+
label: 'Username',
|
|
117
|
+
placeholder: 'This field is disabled',
|
|
118
|
+
disabled: true,
|
|
119
|
+
},
|
|
120
|
+
render: (args) => (
|
|
121
|
+
<FormWrapper>
|
|
122
|
+
<TextInput {...args} />
|
|
123
|
+
</FormWrapper>
|
|
124
|
+
),
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
export const ReadOnly: Story = {
|
|
128
|
+
args: {
|
|
129
|
+
name: 'username',
|
|
130
|
+
label: 'Username',
|
|
131
|
+
fieldValue: 'ReadOnlyValue',
|
|
132
|
+
isReadOnly: true,
|
|
133
|
+
},
|
|
134
|
+
render: (args) => (
|
|
135
|
+
<FormWrapper>
|
|
136
|
+
<TextInput {...args} />
|
|
137
|
+
</FormWrapper>
|
|
138
|
+
),
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
export const SmallSize: Story = {
|
|
142
|
+
args: {
|
|
143
|
+
name: 'username',
|
|
144
|
+
label: 'Username (Small)',
|
|
145
|
+
controlSize: 'sm',
|
|
146
|
+
placeholder: 'Small input',
|
|
147
|
+
},
|
|
148
|
+
render: (args) => (
|
|
149
|
+
<FormWrapper>
|
|
150
|
+
<TextInput {...args} />
|
|
151
|
+
</FormWrapper>
|
|
152
|
+
),
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
export const LargeSize: Story = {
|
|
156
|
+
args: {
|
|
157
|
+
name: 'username',
|
|
158
|
+
label: 'Username (Large)',
|
|
159
|
+
controlSize: 'lg',
|
|
160
|
+
placeholder: 'Large input',
|
|
161
|
+
},
|
|
162
|
+
render: (args) => (
|
|
163
|
+
<FormWrapper>
|
|
164
|
+
<TextInput {...args} />
|
|
165
|
+
</FormWrapper>
|
|
166
|
+
),
|
|
167
|
+
};
|
package/vitest.config.ts
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { fileURLToPath } from 'node:url';
|
|
3
|
+
|
|
4
|
+
import { defineConfig } from 'vitest/config';
|
|
5
|
+
|
|
6
|
+
import { storybookTest } from '@storybook/addon-vitest/vitest-plugin';
|
|
7
|
+
|
|
8
|
+
import { playwright } from '@vitest/browser-playwright';
|
|
9
|
+
|
|
10
|
+
const dirname =
|
|
11
|
+
typeof __dirname !== 'undefined' ? __dirname : path.dirname(fileURLToPath(import.meta.url));
|
|
12
|
+
|
|
13
|
+
// More info at: https://storybook.js.org/docs/next/writing-tests/integrations/vitest-addon
|
|
14
|
+
export default defineConfig({
|
|
15
|
+
test: {
|
|
16
|
+
projects: [
|
|
17
|
+
{
|
|
18
|
+
extends: true,
|
|
19
|
+
plugins: [
|
|
20
|
+
// The plugin will run tests for the stories defined in your Storybook config
|
|
21
|
+
// See options at: https://storybook.js.org/docs/next/writing-tests/integrations/vitest-addon#storybooktest
|
|
22
|
+
storybookTest({ configDir: path.join(dirname, '.storybook') }),
|
|
23
|
+
],
|
|
24
|
+
test: {
|
|
25
|
+
name: 'storybook',
|
|
26
|
+
browser: {
|
|
27
|
+
enabled: true,
|
|
28
|
+
headless: true,
|
|
29
|
+
provider: playwright({}),
|
|
30
|
+
instances: [{ browser: 'chromium' }],
|
|
31
|
+
},
|
|
32
|
+
setupFiles: ['.storybook/vitest.setup.ts'],
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
],
|
|
36
|
+
},
|
|
37
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
/// <reference types="@vitest/browser-playwright" />
|