@licklist/design 0.78.5-dev.41 → 0.78.5-dev.45
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/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +14 -0
- package/dist/v2/components/Alert/Alert.js +87 -0
- package/dist/v2/components/Alert/Alert.scss.js +6 -0
- package/dist/v2/components/Button/Button.d.ts +8 -4
- package/dist/v2/components/Button/Button.d.ts.map +1 -1
- package/dist/v2/components/Button/Button.js +121 -0
- package/dist/v2/components/Button/Button.scss.js +6 -0
- package/dist/v2/components/Button/index.d.ts +2 -2
- package/dist/v2/components/Button/index.d.ts.map +1 -1
- package/dist/v2/components/Checkbox/Checkbox.d.ts +9 -0
- package/dist/v2/components/Checkbox/Checkbox.d.ts.map +1 -0
- package/dist/v2/components/Checkbox/Checkbox.js +231 -0
- package/dist/v2/components/Checkbox/Checkbox.scss.js +6 -0
- package/dist/v2/components/Checkbox/index.d.ts +3 -0
- package/dist/v2/components/Checkbox/index.d.ts.map +1 -0
- package/dist/v2/components/FormField/FormField.d.ts +10 -0
- package/dist/v2/components/FormField/FormField.d.ts.map +1 -0
- package/dist/v2/components/FormField/FormField.js +98 -0
- package/dist/v2/components/FormField/FormField.scss.js +6 -0
- package/dist/v2/components/FormField/index.d.ts +3 -0
- package/dist/v2/components/FormField/index.d.ts.map +1 -0
- package/dist/v2/components/NPSScore/NPSScore.js +546 -0
- package/dist/v2/components/NPSScore/NPSScore.scss.js +6 -0
- package/dist/v2/components/NewInput/NewInput.d.ts +20 -0
- package/dist/v2/components/NewInput/NewInput.d.ts.map +1 -0
- package/dist/v2/components/NewInput/NewInput.js +134 -0
- package/dist/v2/components/NewInput/index.d.ts +2 -0
- package/dist/v2/components/NewInput/index.d.ts.map +1 -0
- package/dist/v2/components/NewPageHeader/NewPageHeader.d.ts +10 -0
- package/dist/v2/components/NewPageHeader/NewPageHeader.d.ts.map +1 -0
- package/dist/v2/components/NewPageHeader/NewPageHeader.js +36 -0
- package/dist/v2/components/NewPageHeader/NewPageHeader.scss.js +6 -0
- package/dist/v2/components/NewPageHeader/index.d.ts +2 -0
- package/dist/v2/components/NewPageHeader/index.d.ts.map +1 -0
- package/dist/v2/components/SectionHeader/SectionHeader.d.ts +8 -0
- package/dist/v2/components/SectionHeader/SectionHeader.d.ts.map +1 -0
- package/dist/v2/components/SectionHeader/SectionHeader.js +13 -0
- package/dist/v2/components/SectionHeader/SectionHeader.scss.js +6 -0
- package/dist/v2/components/SectionHeader/index.d.ts +3 -0
- package/dist/v2/components/SectionHeader/index.d.ts.map +1 -0
- package/dist/v2/components/Select/Select.scss.js +1 -1
- package/dist/v2/components/WYSIWYGEditor/Icons.d.ts +14 -0
- package/dist/v2/components/WYSIWYGEditor/Icons.d.ts.map +1 -0
- package/dist/v2/components/WYSIWYGEditor/Icons.js +192 -0
- package/dist/v2/components/WYSIWYGEditor/WYSIWYGEditor.d.ts +13 -0
- package/dist/v2/components/WYSIWYGEditor/WYSIWYGEditor.d.ts.map +1 -0
- package/dist/v2/components/WYSIWYGEditor/WYSIWYGEditor.js +219 -0
- package/dist/v2/components/WYSIWYGEditor/WYSIWYGEditor.scss.js +6 -0
- package/dist/v2/components/WYSIWYGEditor/index.d.ts +3 -0
- package/dist/v2/components/WYSIWYGEditor/index.d.ts.map +1 -0
- package/dist/v2/components/index.d.ts +22 -0
- package/dist/v2/components/index.d.ts.map +1 -0
- package/dist/v2/styles/components/Button.scss +51 -53
- package/dist/v2/styles/form/Layout.scss +15 -0
- package/dist/v2/styles/form/NewInput.scss +76 -53
- package/dist/v2/styles/form/NewInput.scss.js +6 -0
- package/dist/v2/styles/index.scss +1 -0
- package/dist/v2/styles/tokens/_colors.scss +6 -6
- package/dist/v2/styles/tokens/_typography.scss +2 -2
- package/package.json +3 -3
- package/src/index.ts +1 -0
- package/src/v2/components/Alert/Alert.scss +3 -3
- package/src/v2/components/Button/Button.tsx +34 -12
- package/src/v2/components/Button/index.ts +2 -2
- package/src/v2/components/Checkbox/Checkbox.scss +205 -0
- package/src/v2/components/Checkbox/Checkbox.stories.tsx +316 -0
- package/src/v2/components/Checkbox/Checkbox.tsx +106 -0
- package/src/v2/components/Checkbox/index.ts +3 -0
- package/src/v2/components/FormField/FormField.scss +87 -0
- package/src/v2/components/FormField/FormField.stories.tsx +71 -0
- package/src/v2/components/FormField/FormField.tsx +37 -0
- package/src/v2/components/FormField/index.ts +3 -0
- package/src/v2/components/NewInput/NewInput.stories.tsx +433 -0
- package/src/v2/components/NewInput/NewInput.tsx +96 -0
- package/src/v2/components/NewInput/index.ts +1 -0
- package/src/v2/components/NewPageHeader/NewPageHeader.scss +47 -0
- package/src/v2/components/NewPageHeader/NewPageHeader.stories.tsx +44 -0
- package/src/v2/components/NewPageHeader/NewPageHeader.tsx +35 -0
- package/src/v2/components/NewPageHeader/index.ts +1 -0
- package/src/v2/components/SectionHeader/SectionHeader.scss +11 -0
- package/src/v2/components/SectionHeader/SectionHeader.tsx +15 -0
- package/src/v2/components/SectionHeader/index.ts +2 -0
- package/src/v2/components/Select/Select.scss +5 -5
- package/src/v2/components/WYSIWYGEditor/Icons.tsx +81 -0
- package/src/v2/components/WYSIWYGEditor/WYSIWYGEditor.scss +318 -0
- package/src/v2/components/WYSIWYGEditor/WYSIWYGEditor.stories.tsx +252 -0
- package/src/v2/components/WYSIWYGEditor/WYSIWYGEditor.tsx +237 -0
- package/src/v2/components/WYSIWYGEditor/index.ts +3 -0
- package/src/v2/components/index.ts +37 -0
- package/src/v2/styles/components/Button.scss +51 -53
- package/src/v2/styles/form/Layout.scss +15 -0
- package/src/v2/styles/form/NewInput.scss +76 -53
- package/src/v2/styles/index.scss +1 -0
- package/src/v2/styles/tokens/_colors.scss +6 -6
- package/src/v2/styles/tokens/_typography.scss +2 -2
|
@@ -0,0 +1,433 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react'
|
|
2
|
+
import { NewInput } from './NewInput'
|
|
3
|
+
import { useState } from 'react'
|
|
4
|
+
|
|
5
|
+
const meta: Meta<typeof NewInput> = {
|
|
6
|
+
title: 'V2/Components/NewInput',
|
|
7
|
+
component: NewInput,
|
|
8
|
+
parameters: {
|
|
9
|
+
layout: 'centered',
|
|
10
|
+
},
|
|
11
|
+
tags: ['autodocs'],
|
|
12
|
+
argTypes: {
|
|
13
|
+
as: {
|
|
14
|
+
control: 'radio',
|
|
15
|
+
options: ['input', 'textarea'],
|
|
16
|
+
},
|
|
17
|
+
type: {
|
|
18
|
+
control: 'select',
|
|
19
|
+
options: ['text', 'email', 'password', 'number', 'tel', 'url', 'search', 'date', 'time'],
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export default meta
|
|
25
|
+
type Story = StoryObj<typeof NewInput>
|
|
26
|
+
|
|
27
|
+
// Basic Input Stories
|
|
28
|
+
export const Default: Story = {
|
|
29
|
+
args: {
|
|
30
|
+
placeholder: 'Enter text...',
|
|
31
|
+
},
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export const WithLabel: Story = {
|
|
35
|
+
args: {
|
|
36
|
+
label: 'Full Name',
|
|
37
|
+
placeholder: 'John Doe',
|
|
38
|
+
},
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export const WithHelperText: Story = {
|
|
42
|
+
args: {
|
|
43
|
+
label: 'Email Address',
|
|
44
|
+
placeholder: 'example@email.com',
|
|
45
|
+
helperText: 'We will never share your email with anyone else.',
|
|
46
|
+
},
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export const OptionalField: Story = {
|
|
50
|
+
args: {
|
|
51
|
+
label: 'Middle Name',
|
|
52
|
+
optional: true,
|
|
53
|
+
placeholder: 'Optional middle name',
|
|
54
|
+
},
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export const WithError: Story = {
|
|
58
|
+
args: {
|
|
59
|
+
label: 'Username',
|
|
60
|
+
placeholder: 'Enter username',
|
|
61
|
+
error: 'This field is required',
|
|
62
|
+
},
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export const WithValue: Story = {
|
|
66
|
+
args: {
|
|
67
|
+
label: 'Waiver Template Name',
|
|
68
|
+
value: 'Standard Waiver Template',
|
|
69
|
+
helperText: 'The name of your waiver template',
|
|
70
|
+
},
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Input Types
|
|
74
|
+
export const EmailInput: Story = {
|
|
75
|
+
args: {
|
|
76
|
+
label: 'Email',
|
|
77
|
+
type: 'email',
|
|
78
|
+
placeholder: 'your.email@example.com',
|
|
79
|
+
helperText: 'Enter a valid email address',
|
|
80
|
+
},
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export const PasswordInput: Story = {
|
|
84
|
+
args: {
|
|
85
|
+
label: 'Password',
|
|
86
|
+
type: 'password',
|
|
87
|
+
placeholder: 'Enter your password',
|
|
88
|
+
helperText: 'Must be at least 8 characters',
|
|
89
|
+
},
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export const NumberInput: Story = {
|
|
93
|
+
args: {
|
|
94
|
+
label: 'Expires In',
|
|
95
|
+
type: 'number',
|
|
96
|
+
placeholder: '12',
|
|
97
|
+
helperText: 'Number of months',
|
|
98
|
+
min: 1,
|
|
99
|
+
max: 120,
|
|
100
|
+
},
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export const PhoneInput: Story = {
|
|
104
|
+
args: {
|
|
105
|
+
label: 'Phone Number',
|
|
106
|
+
type: 'tel',
|
|
107
|
+
placeholder: '+1 (555) 123-4567',
|
|
108
|
+
},
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export const URLInput: Story = {
|
|
112
|
+
args: {
|
|
113
|
+
label: 'Website',
|
|
114
|
+
type: 'url',
|
|
115
|
+
placeholder: 'https://example.com',
|
|
116
|
+
helperText: 'Enter a valid URL',
|
|
117
|
+
},
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export const DateInput: Story = {
|
|
121
|
+
args: {
|
|
122
|
+
label: 'Date of Birth',
|
|
123
|
+
type: 'date',
|
|
124
|
+
},
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export const SearchInput: Story = {
|
|
128
|
+
args: {
|
|
129
|
+
label: 'Search',
|
|
130
|
+
type: 'search',
|
|
131
|
+
placeholder: 'Search for waivers...',
|
|
132
|
+
},
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Textarea
|
|
136
|
+
export const TextareaBasic: Story = {
|
|
137
|
+
args: {
|
|
138
|
+
label: 'Description',
|
|
139
|
+
as: 'textarea',
|
|
140
|
+
placeholder: 'Enter a detailed description...',
|
|
141
|
+
} as any,
|
|
142
|
+
render: (args) => <NewInput {...(args as any)} rows={5} />,
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
export const TextareaWithHelperText: Story = {
|
|
146
|
+
args: {
|
|
147
|
+
label: 'Waiver Text',
|
|
148
|
+
as: 'textarea',
|
|
149
|
+
placeholder: 'Enter the waiver content...',
|
|
150
|
+
helperText: 'This will be displayed to users when signing the waiver',
|
|
151
|
+
} as any,
|
|
152
|
+
render: (args) => <NewInput {...(args as any)} rows={8} />,
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
export const TextareaWithError: Story = {
|
|
156
|
+
args: {
|
|
157
|
+
label: 'Comments',
|
|
158
|
+
as: 'textarea',
|
|
159
|
+
placeholder: 'Enter your comments...',
|
|
160
|
+
error: 'Comments are required',
|
|
161
|
+
} as any,
|
|
162
|
+
render: (args) => <NewInput {...(args as any)} rows={4} />,
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// States
|
|
166
|
+
export const DisabledInput: Story = {
|
|
167
|
+
args: {
|
|
168
|
+
label: 'Disabled Field',
|
|
169
|
+
placeholder: 'Cannot edit this',
|
|
170
|
+
disabled: true,
|
|
171
|
+
helperText: 'This field is disabled',
|
|
172
|
+
},
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
export const DisabledWithValue: Story = {
|
|
176
|
+
args: {
|
|
177
|
+
label: 'Read-only Field',
|
|
178
|
+
value: 'This value cannot be changed',
|
|
179
|
+
disabled: true,
|
|
180
|
+
},
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
export const ReadOnlyInput: Story = {
|
|
184
|
+
args: {
|
|
185
|
+
label: 'Read Only',
|
|
186
|
+
value: 'Read-only value',
|
|
187
|
+
readOnly: true,
|
|
188
|
+
helperText: 'This field is read-only',
|
|
189
|
+
},
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
export const RequiredField: Story = {
|
|
193
|
+
args: {
|
|
194
|
+
label: 'Required Field',
|
|
195
|
+
required: true,
|
|
196
|
+
placeholder: 'This field is required',
|
|
197
|
+
helperText: 'You must fill out this field',
|
|
198
|
+
},
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// With Icon
|
|
202
|
+
export const WithIcon: Story = {
|
|
203
|
+
args: {
|
|
204
|
+
label: 'Search',
|
|
205
|
+
placeholder: 'Search...',
|
|
206
|
+
icon: (
|
|
207
|
+
<svg
|
|
208
|
+
width="20"
|
|
209
|
+
height="20"
|
|
210
|
+
viewBox="0 0 20 20"
|
|
211
|
+
fill="none"
|
|
212
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
213
|
+
>
|
|
214
|
+
<path
|
|
215
|
+
d="M9 17A8 8 0 1 0 9 1a8 8 0 0 0 0 16zM18 18l-4.35-4.35"
|
|
216
|
+
stroke="currentColor"
|
|
217
|
+
strokeWidth="2"
|
|
218
|
+
strokeLinecap="round"
|
|
219
|
+
strokeLinejoin="round"
|
|
220
|
+
/>
|
|
221
|
+
</svg>
|
|
222
|
+
),
|
|
223
|
+
},
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// Controlled Input Example
|
|
227
|
+
export const Controlled: Story = {
|
|
228
|
+
render: () => {
|
|
229
|
+
const ControlledExample = () => {
|
|
230
|
+
const [value, setValue] = useState('')
|
|
231
|
+
const [error, setError] = useState('')
|
|
232
|
+
|
|
233
|
+
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
|
234
|
+
const newValue = e.target.value
|
|
235
|
+
setValue(newValue)
|
|
236
|
+
|
|
237
|
+
// Validation
|
|
238
|
+
if (newValue.length > 0 && newValue.length < 3) {
|
|
239
|
+
setError('Must be at least 3 characters')
|
|
240
|
+
} else {
|
|
241
|
+
setError('')
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
return (
|
|
246
|
+
<div style={{ display: 'flex', flexDirection: 'column', gap: '20px', minWidth: '400px' }}>
|
|
247
|
+
<NewInput
|
|
248
|
+
label="Controlled Input"
|
|
249
|
+
placeholder="Type at least 3 characters..."
|
|
250
|
+
value={value}
|
|
251
|
+
onChange={handleChange}
|
|
252
|
+
error={error}
|
|
253
|
+
helperText={!error ? `Character count: ${value.length}` : undefined}
|
|
254
|
+
/>
|
|
255
|
+
<div style={{ padding: '10px', background: '#f0f0f0', borderRadius: '4px' }}>
|
|
256
|
+
<strong>Current value:</strong>
|
|
257
|
+
<pre>{JSON.stringify(value, null, 2)}</pre>
|
|
258
|
+
</div>
|
|
259
|
+
</div>
|
|
260
|
+
)
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
return <ControlledExample />
|
|
264
|
+
},
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
// Form Example
|
|
268
|
+
export const FormExample: Story = {
|
|
269
|
+
render: () => {
|
|
270
|
+
const FormExampleComponent = () => {
|
|
271
|
+
const [formData, setFormData] = useState({
|
|
272
|
+
name: '',
|
|
273
|
+
email: '',
|
|
274
|
+
expiresIn: '',
|
|
275
|
+
description: '',
|
|
276
|
+
})
|
|
277
|
+
const [errors, setErrors] = useState<Record<string, string>>({})
|
|
278
|
+
|
|
279
|
+
const handleChange = (field: string) => (e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => {
|
|
280
|
+
setFormData({ ...formData, [field]: e.target.value })
|
|
281
|
+
// Clear error when user starts typing
|
|
282
|
+
if (errors[field]) {
|
|
283
|
+
setErrors({ ...errors, [field]: '' })
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
const handleSubmit = (e: React.FormEvent) => {
|
|
288
|
+
e.preventDefault()
|
|
289
|
+
const newErrors: Record<string, string> = {}
|
|
290
|
+
|
|
291
|
+
if (!formData.name) newErrors.name = 'Name is required'
|
|
292
|
+
if (!formData.email) newErrors.email = 'Email is required'
|
|
293
|
+
else if (!/\S+@\S+\.\S+/.test(formData.email)) newErrors.email = 'Email is invalid'
|
|
294
|
+
if (!formData.expiresIn) newErrors.expiresIn = 'Expiration period is required'
|
|
295
|
+
|
|
296
|
+
setErrors(newErrors)
|
|
297
|
+
|
|
298
|
+
if (Object.keys(newErrors).length === 0) {
|
|
299
|
+
alert('Form submitted successfully!')
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
return (
|
|
304
|
+
<form onSubmit={handleSubmit} style={{ display: 'flex', flexDirection: 'column', gap: '20px', minWidth: '500px' }}>
|
|
305
|
+
<h3>Waiver Template Form (Example)</h3>
|
|
306
|
+
|
|
307
|
+
<NewInput
|
|
308
|
+
label="Template Name"
|
|
309
|
+
placeholder="Enter template name"
|
|
310
|
+
value={formData.name}
|
|
311
|
+
onChange={handleChange('name')}
|
|
312
|
+
error={errors.name}
|
|
313
|
+
helperText="A descriptive name for your waiver template"
|
|
314
|
+
/>
|
|
315
|
+
|
|
316
|
+
<NewInput
|
|
317
|
+
label="Contact Email"
|
|
318
|
+
type="email"
|
|
319
|
+
placeholder="contact@example.com"
|
|
320
|
+
value={formData.email}
|
|
321
|
+
onChange={handleChange('email')}
|
|
322
|
+
error={errors.email}
|
|
323
|
+
helperText="Email for waiver-related notifications"
|
|
324
|
+
/>
|
|
325
|
+
|
|
326
|
+
<NewInput
|
|
327
|
+
label="Expires In (months)"
|
|
328
|
+
type="number"
|
|
329
|
+
placeholder="12"
|
|
330
|
+
value={formData.expiresIn}
|
|
331
|
+
onChange={handleChange('expiresIn')}
|
|
332
|
+
error={errors.expiresIn}
|
|
333
|
+
helperText="How many months until the waiver expires"
|
|
334
|
+
min={1}
|
|
335
|
+
max={120}
|
|
336
|
+
/>
|
|
337
|
+
|
|
338
|
+
<NewInput
|
|
339
|
+
label="Description"
|
|
340
|
+
optional={true}
|
|
341
|
+
as="textarea"
|
|
342
|
+
placeholder="Optional description..."
|
|
343
|
+
value={formData.description}
|
|
344
|
+
onChange={handleChange('description') as any}
|
|
345
|
+
rows={4}
|
|
346
|
+
helperText="Additional notes about this template"
|
|
347
|
+
/>
|
|
348
|
+
|
|
349
|
+
<button
|
|
350
|
+
type="submit"
|
|
351
|
+
style={{
|
|
352
|
+
padding: '10px 20px',
|
|
353
|
+
background: '#14215A',
|
|
354
|
+
color: 'white',
|
|
355
|
+
border: 'none',
|
|
356
|
+
borderRadius: '4px',
|
|
357
|
+
cursor: 'pointer',
|
|
358
|
+
fontSize: '14px',
|
|
359
|
+
fontWeight: 500,
|
|
360
|
+
}}
|
|
361
|
+
>
|
|
362
|
+
Submit Form
|
|
363
|
+
</button>
|
|
364
|
+
|
|
365
|
+
<div style={{ padding: '10px', background: '#f0f0f0', borderRadius: '4px' }}>
|
|
366
|
+
<strong>Form Data:</strong>
|
|
367
|
+
<pre>{JSON.stringify(formData, null, 2)}</pre>
|
|
368
|
+
</div>
|
|
369
|
+
</form>
|
|
370
|
+
)
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
return <FormExampleComponent />
|
|
374
|
+
},
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
// Size Variations
|
|
378
|
+
export const SmallWidth: Story = {
|
|
379
|
+
args: {
|
|
380
|
+
label: 'Small Input',
|
|
381
|
+
placeholder: 'Small width',
|
|
382
|
+
style: { width: '200px' },
|
|
383
|
+
},
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
export const MediumWidth: Story = {
|
|
387
|
+
args: {
|
|
388
|
+
label: 'Medium Input',
|
|
389
|
+
placeholder: 'Medium width',
|
|
390
|
+
style: { width: '400px' },
|
|
391
|
+
},
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
export const FullWidth: Story = {
|
|
395
|
+
args: {
|
|
396
|
+
label: 'Full Width Input',
|
|
397
|
+
placeholder: 'Full width',
|
|
398
|
+
style: { width: '100%' },
|
|
399
|
+
},
|
|
400
|
+
decorators: [
|
|
401
|
+
(Story) => (
|
|
402
|
+
<div style={{ width: '600px' }}>
|
|
403
|
+
<Story />
|
|
404
|
+
</div>
|
|
405
|
+
),
|
|
406
|
+
],
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
// Edge Cases
|
|
410
|
+
export const LongLabel: Story = {
|
|
411
|
+
args: {
|
|
412
|
+
label: 'This is a very long label that might wrap to multiple lines depending on the container width',
|
|
413
|
+
placeholder: 'Enter value',
|
|
414
|
+
helperText: 'This is also a very long helper text that provides detailed information about what should be entered in this field',
|
|
415
|
+
},
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
export const LongError: Story = {
|
|
419
|
+
args: {
|
|
420
|
+
label: 'Field with long error',
|
|
421
|
+
placeholder: 'Enter value',
|
|
422
|
+
error: 'This is a very long error message that explains in detail what went wrong and how to fix it. It should wrap properly.',
|
|
423
|
+
},
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
export const MaxLengthInput: Story = {
|
|
427
|
+
args: {
|
|
428
|
+
label: 'Limited Characters',
|
|
429
|
+
placeholder: 'Max 50 characters',
|
|
430
|
+
maxLength: 50,
|
|
431
|
+
helperText: 'You can only enter up to 50 characters',
|
|
432
|
+
},
|
|
433
|
+
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import React, {InputHTMLAttributes, TextareaHTMLAttributes, forwardRef, ReactNode} from 'react';
|
|
2
|
+
import '../../styles/form/NewInput.scss';
|
|
3
|
+
|
|
4
|
+
type CommonInputProps = {
|
|
5
|
+
label?: string;
|
|
6
|
+
optional?: boolean;
|
|
7
|
+
helperText?: string;
|
|
8
|
+
error?: string;
|
|
9
|
+
icon?: ReactNode;
|
|
10
|
+
required?: boolean;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
type InputProps = CommonInputProps & {
|
|
14
|
+
as?: 'input';
|
|
15
|
+
} & InputHTMLAttributes<HTMLInputElement>
|
|
16
|
+
|
|
17
|
+
type TextareaProps = CommonInputProps & {
|
|
18
|
+
as: 'textarea';
|
|
19
|
+
} & TextareaHTMLAttributes<HTMLTextAreaElement>
|
|
20
|
+
|
|
21
|
+
export type NewInputProps = InputProps | TextareaProps
|
|
22
|
+
|
|
23
|
+
export const NewInput = forwardRef<HTMLInputElement | HTMLTextAreaElement, NewInputProps>(
|
|
24
|
+
({label, optional, helperText, error, icon, as = 'input', required, className = '', ...props}, ref) => {
|
|
25
|
+
const isError = !!error;
|
|
26
|
+
const containerClasses = [
|
|
27
|
+
'new-form-input',
|
|
28
|
+
isError ? 'new-form-input--error' : '',
|
|
29
|
+
props.disabled ? 'new-form-input--disabled' : '',
|
|
30
|
+
className
|
|
31
|
+
].filter(Boolean).join(' ');
|
|
32
|
+
|
|
33
|
+
const renderInput = () => {
|
|
34
|
+
if (as === 'textarea') {
|
|
35
|
+
return (
|
|
36
|
+
<textarea
|
|
37
|
+
ref={ref as React.Ref<HTMLTextAreaElement>}
|
|
38
|
+
className="new-form-input__textarea"
|
|
39
|
+
{...(props as React.TextareaHTMLAttributes<HTMLTextAreaElement>)}
|
|
40
|
+
/>
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (icon) {
|
|
45
|
+
return (
|
|
46
|
+
<div className="new-form-input__input-with-icon">
|
|
47
|
+
<input
|
|
48
|
+
ref={ref as React.Ref<HTMLInputElement>}
|
|
49
|
+
{...(props as React.InputHTMLAttributes<HTMLInputElement>)}
|
|
50
|
+
/>
|
|
51
|
+
<div className="icon">{icon}</div>
|
|
52
|
+
</div>
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return (
|
|
57
|
+
<input
|
|
58
|
+
ref={ref as React.Ref<HTMLInputElement>}
|
|
59
|
+
className="new-form-input__input"
|
|
60
|
+
{...(props as React.InputHTMLAttributes<HTMLInputElement>)}
|
|
61
|
+
/>
|
|
62
|
+
);
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
return (
|
|
66
|
+
<div className={containerClasses}>
|
|
67
|
+
{(label || optional) && (
|
|
68
|
+
<div className="new-form-input__label-row">
|
|
69
|
+
{label && (
|
|
70
|
+
<label className="new-form-input__label">
|
|
71
|
+
{label}
|
|
72
|
+
</label>
|
|
73
|
+
)}
|
|
74
|
+
{optional && <span className="new-form-input__label-optional">Optional</span>}
|
|
75
|
+
</div>
|
|
76
|
+
)}
|
|
77
|
+
|
|
78
|
+
{renderInput()}
|
|
79
|
+
|
|
80
|
+
{helperText && (
|
|
81
|
+
<p className="new-form-input__helper-text">
|
|
82
|
+
{helperText}
|
|
83
|
+
</p>
|
|
84
|
+
)}
|
|
85
|
+
|
|
86
|
+
{error && error !== ' ' && (
|
|
87
|
+
<p className="new-form-input__error-text">
|
|
88
|
+
{error}
|
|
89
|
+
</p>
|
|
90
|
+
)}
|
|
91
|
+
</div>
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
);
|
|
95
|
+
|
|
96
|
+
NewInput.displayName = 'NewInput';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './NewInput';
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
.new-page-header-container {
|
|
2
|
+
display: flex;
|
|
3
|
+
flex-direction: column;
|
|
4
|
+
align-items: flex-start;
|
|
5
|
+
gap: 24px;
|
|
6
|
+
align-self: stretch;
|
|
7
|
+
padding-left: 32px;
|
|
8
|
+
padding-right: 32px;
|
|
9
|
+
|
|
10
|
+
@media (max-width: 768px) {
|
|
11
|
+
padding-left: 16px;
|
|
12
|
+
padding-right: 16px;
|
|
13
|
+
gap: 16px;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
.new-page-header {
|
|
18
|
+
display: flex;
|
|
19
|
+
justify-content: space-between;
|
|
20
|
+
align-items: center;
|
|
21
|
+
align-self: stretch;
|
|
22
|
+
width: 100%;
|
|
23
|
+
gap: 16px;
|
|
24
|
+
|
|
25
|
+
@media (max-width: 768px) {
|
|
26
|
+
gap: 8px;
|
|
27
|
+
padding-top: 16px;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
&__title {
|
|
31
|
+
color: var(--label-primary);
|
|
32
|
+
font-family: 'Geist', sans-serif;
|
|
33
|
+
font-size: 24px;
|
|
34
|
+
font-style: normal;
|
|
35
|
+
font-weight: 600;
|
|
36
|
+
line-height: 28px;
|
|
37
|
+
margin: 0;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
.new-page-header__divider {
|
|
42
|
+
width: 100%;
|
|
43
|
+
height: 1px;
|
|
44
|
+
border: none;
|
|
45
|
+
background-color: var(--border-primary, #e8e9ef);
|
|
46
|
+
margin: 0;
|
|
47
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react'
|
|
2
|
+
import { NewPageHeader } from './NewPageHeader'
|
|
3
|
+
|
|
4
|
+
const meta: Meta<typeof NewPageHeader> = {
|
|
5
|
+
title: 'V2/Components/NewPageHeader',
|
|
6
|
+
component: NewPageHeader,
|
|
7
|
+
parameters: {
|
|
8
|
+
layout: 'padded',
|
|
9
|
+
},
|
|
10
|
+
tags: ['autodocs'],
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export default meta
|
|
14
|
+
type Story = StoryObj<typeof NewPageHeader>
|
|
15
|
+
|
|
16
|
+
export const Default: Story = {
|
|
17
|
+
args: {
|
|
18
|
+
title: 'New waiver',
|
|
19
|
+
cancelLabel: 'Cancel',
|
|
20
|
+
onCancel: () => alert('Cancel clicked'),
|
|
21
|
+
},
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export const WithoutCancelButton: Story = {
|
|
25
|
+
args: {
|
|
26
|
+
title: 'View Only Page',
|
|
27
|
+
},
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export const LongTitle: Story = {
|
|
31
|
+
args: {
|
|
32
|
+
title: 'This is a very long page title that might wrap on smaller screens',
|
|
33
|
+
cancelLabel: 'Cancel',
|
|
34
|
+
onCancel: () => alert('Cancel clicked'),
|
|
35
|
+
},
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export const CustomCancelLabel: Story = {
|
|
39
|
+
args: {
|
|
40
|
+
title: 'Edit Waiver Template',
|
|
41
|
+
cancelLabel: 'Go Back',
|
|
42
|
+
onCancel: () => alert('Go Back clicked'),
|
|
43
|
+
},
|
|
44
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { Button, ButtonText } from '../Button';
|
|
3
|
+
import './NewPageHeader.scss';
|
|
4
|
+
|
|
5
|
+
export interface NewPageHeaderProps {
|
|
6
|
+
title: string;
|
|
7
|
+
cancelLabel?: string;
|
|
8
|
+
onCancel?: () => void;
|
|
9
|
+
className?: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export const NewPageHeader: React.FC<NewPageHeaderProps> = ({
|
|
13
|
+
title,
|
|
14
|
+
cancelLabel = 'Cancel',
|
|
15
|
+
onCancel,
|
|
16
|
+
className = ''
|
|
17
|
+
}) => {
|
|
18
|
+
return (
|
|
19
|
+
<div className={`new-page-header-container ${className}`}>
|
|
20
|
+
<div className="new-page-header">
|
|
21
|
+
<h1 className="new-page-header__title">{title}</h1>
|
|
22
|
+
{onCancel && (
|
|
23
|
+
<Button
|
|
24
|
+
type="button"
|
|
25
|
+
variant="destructive-soft"
|
|
26
|
+
onClick={onCancel}
|
|
27
|
+
>
|
|
28
|
+
<ButtonText color="danger">{cancelLabel}</ButtonText>
|
|
29
|
+
</Button>
|
|
30
|
+
)}
|
|
31
|
+
</div>
|
|
32
|
+
<hr className="new-page-header__divider" />
|
|
33
|
+
</div>
|
|
34
|
+
);
|
|
35
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './NewPageHeader';
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
.section-header {
|
|
2
|
+
display: flex;
|
|
3
|
+
font-family: var(--font-family-sans, 'Geist', sans-serif);
|
|
4
|
+
font-size: 17px;
|
|
5
|
+
font-style: normal;
|
|
6
|
+
font-weight: 600;
|
|
7
|
+
line-height: 20px;
|
|
8
|
+
margin: 0;
|
|
9
|
+
align-self: stretch;
|
|
10
|
+
color: var(--label-primary, #121E52);
|
|
11
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import './SectionHeader.scss';
|
|
3
|
+
|
|
4
|
+
export interface SectionHeaderProps {
|
|
5
|
+
title: string;
|
|
6
|
+
className?: string;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export const SectionHeader: React.FC<SectionHeaderProps> = ({ title, className = '' }) => {
|
|
10
|
+
return (
|
|
11
|
+
<span className={`section-header ${className}`}>
|
|
12
|
+
{title}
|
|
13
|
+
</span>
|
|
14
|
+
);
|
|
15
|
+
};
|