@windstream/react-shared-components 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/LICENSE +21 -0
- package/README.md +360 -0
- package/dist/core.d.ts +389 -0
- package/dist/index.d.ts +389 -0
- package/dist/index.esm.js +35 -0
- package/dist/index.esm.js.map +1 -0
- package/dist/index.js +35 -0
- package/dist/index.js.map +1 -0
- package/dist/styles.css +3 -0
- package/dist/utils/index.d.ts +11 -0
- package/dist/utils/index.esm.js +2 -0
- package/dist/utils/index.esm.js.map +1 -0
- package/dist/utils/index.js +2 -0
- package/dist/utils/index.js.map +1 -0
- package/package.json +151 -0
- package/src/components/accordion/Accordion.stories.tsx +225 -0
- package/src/components/accordion/index.tsx +36 -0
- package/src/components/accordion/types.ts +10 -0
- package/src/components/alert-card/AlertCard.stories.tsx +172 -0
- package/src/components/alert-card/index.tsx +35 -0
- package/src/components/alert-card/types.ts +9 -0
- package/src/components/brand-button/BrandButton.stories.tsx +221 -0
- package/src/components/brand-button/helpers.ts +35 -0
- package/src/components/brand-button/index.tsx +90 -0
- package/src/components/brand-button/types.ts +27 -0
- package/src/components/button/Button.stories.tsx +108 -0
- package/src/components/button/index.tsx +23 -0
- package/src/components/button/types.ts +14 -0
- package/src/components/call-button/CallButton.stories.tsx +324 -0
- package/src/components/call-button/index.tsx +80 -0
- package/src/components/call-button/types.ts +9 -0
- package/src/components/checkbox/Checkbox.stories.tsx +248 -0
- package/src/components/checkbox/index.tsx +185 -0
- package/src/components/checkbox/types.ts +24 -0
- package/src/components/checklist/Checklist.stories.tsx +151 -0
- package/src/components/checklist/index.tsx +33 -0
- package/src/components/checklist/types.ts +6 -0
- package/src/components/collapse/Collapse.stories.tsx +256 -0
- package/src/components/collapse/index.tsx +44 -0
- package/src/components/collapse/types.ts +6 -0
- package/src/components/divider/Divider.stories.tsx +206 -0
- package/src/components/divider/index.tsx +23 -0
- package/src/components/divider/type.ts +3 -0
- package/src/components/input/Input.stories.tsx +358 -0
- package/src/components/input/index.tsx +174 -0
- package/src/components/input/types.ts +37 -0
- package/src/components/link/Link.stories.tsx +163 -0
- package/src/components/link/index.tsx +96 -0
- package/src/components/link/types.ts +25 -0
- package/src/components/list/List.stories.tsx +272 -0
- package/src/components/list/index.tsx +86 -0
- package/src/components/list/list-item/index.tsx +36 -0
- package/src/components/list/list-item/types.ts +13 -0
- package/src/components/list/types.ts +29 -0
- package/src/components/material-icon/MaterialIcon.stories.tsx +330 -0
- package/src/components/material-icon/constants.ts +95 -0
- package/src/components/material-icon/index.tsx +44 -0
- package/src/components/material-icon/types.ts +31 -0
- package/src/components/modal/Modal.stories.tsx +171 -0
- package/src/components/modal/index.tsx +168 -0
- package/src/components/modal/types.ts +24 -0
- package/src/components/radio-button/RadioButton.stories.tsx +307 -0
- package/src/components/radio-button/index.tsx +73 -0
- package/src/components/radio-button/types.ts +22 -0
- package/src/components/see-more/SeeMore.stories.tsx +182 -0
- package/src/components/see-more/index.tsx +38 -0
- package/src/components/see-more/types.ts +4 -0
- package/src/components/select/Select.stories.tsx +410 -0
- package/src/components/select/index.tsx +150 -0
- package/src/components/select/types.ts +34 -0
- package/src/components/select-plan-button/SelectPlanButton.stories.tsx +160 -0
- package/src/components/select-plan-button/index.tsx +21 -0
- package/src/components/select-plan-button/types.ts +4 -0
- package/src/components/skeleton/Skeleton.stories.tsx +180 -0
- package/src/components/skeleton/index.tsx +61 -0
- package/src/components/skeleton/types.ts +4 -0
- package/src/components/spinner/Spinner.stories.tsx +335 -0
- package/src/components/spinner/index.tsx +44 -0
- package/src/components/spinner/types.ts +5 -0
- package/src/components/text/Text.stories.tsx +302 -0
- package/src/components/text/index.tsx +26 -0
- package/src/components/text/types.ts +45 -0
- package/src/components/tooltip/Tooltip.stories.tsx +220 -0
- package/src/components/tooltip/index.tsx +78 -0
- package/src/components/tooltip/types.ts +7 -0
- package/src/components/view-cart-button/ViewCartButton.stories.tsx +241 -0
- package/src/components/view-cart-button/index.tsx +39 -0
- package/src/components/view-cart-button/types.ts +5 -0
- package/src/hooks/use-body-scroll-lock.ts +31 -0
- package/src/index.ts +81 -0
- package/src/setupTests.ts +46 -0
- package/src/stories/DocsTemplate.tsx +26 -0
- package/src/stories/assets/accessibility.png +0 -0
- package/src/stories/assets/accessibility.svg +1 -0
- package/src/stories/assets/addon-library.png +0 -0
- package/src/stories/assets/assets.png +0 -0
- package/src/stories/assets/avif-test-image.avif +0 -0
- package/src/stories/assets/context.png +0 -0
- package/src/stories/assets/discord.svg +1 -0
- package/src/stories/assets/docs.png +0 -0
- package/src/stories/assets/figma-plugin.png +0 -0
- package/src/stories/assets/github.svg +1 -0
- package/src/stories/assets/share.png +0 -0
- package/src/stories/assets/styling.png +0 -0
- package/src/stories/assets/testing.png +0 -0
- package/src/stories/assets/theming.png +0 -0
- package/src/stories/assets/tutorials.svg +1 -0
- package/src/stories/assets/youtube.svg +1 -0
- package/src/styles/globals.css +50 -0
- package/src/types/global.d.ts +9 -0
- package/src/utils/index.ts +49 -0
- package/tailwind.config.cjs +524 -0
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { forwardRef } from "react";
|
|
2
|
+
import { cx } from "@shared/utils";
|
|
3
|
+
import { ButtonProps } from "@shared/components/button/types";
|
|
4
|
+
// Minimal button: styling is entirely up to the consumer via className
|
|
5
|
+
export const Button = forwardRef<HTMLButtonElement, ButtonProps>(
|
|
6
|
+
({ children, className = "", style, disabled, ...props }, ref) => {
|
|
7
|
+
return (
|
|
8
|
+
<button
|
|
9
|
+
ref={ref}
|
|
10
|
+
className={cx(className)}
|
|
11
|
+
style={style}
|
|
12
|
+
disabled={disabled}
|
|
13
|
+
{...props}
|
|
14
|
+
>
|
|
15
|
+
{children}
|
|
16
|
+
</button>
|
|
17
|
+
);
|
|
18
|
+
}
|
|
19
|
+
);
|
|
20
|
+
|
|
21
|
+
Button.displayName = "Button";
|
|
22
|
+
|
|
23
|
+
export type { ButtonProps };
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import {
|
|
2
|
+
type ButtonHTMLAttributes,
|
|
3
|
+
type CSSProperties,
|
|
4
|
+
type ReactNode,
|
|
5
|
+
} from "react";
|
|
6
|
+
|
|
7
|
+
export interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
|
|
8
|
+
/** Content to render inside the button */
|
|
9
|
+
children?: ReactNode;
|
|
10
|
+
/** Custom className for the button */
|
|
11
|
+
className?: string;
|
|
12
|
+
/** Custom styles */
|
|
13
|
+
style?: CSSProperties;
|
|
14
|
+
}
|
|
@@ -0,0 +1,324 @@
|
|
|
1
|
+
import { CallButton } from "./index";
|
|
2
|
+
import { DocsPage } from "@shared/stories/DocsTemplate";
|
|
3
|
+
|
|
4
|
+
import type { Meta, StoryObj } from "@storybook/react";
|
|
5
|
+
|
|
6
|
+
const meta: Meta<typeof CallButton> = {
|
|
7
|
+
title: "Components/CallButton",
|
|
8
|
+
component: CallButton,
|
|
9
|
+
tags: ["autodocs"],
|
|
10
|
+
parameters: {
|
|
11
|
+
layout: "centered",
|
|
12
|
+
docs: {
|
|
13
|
+
page: DocsPage,
|
|
14
|
+
description: {
|
|
15
|
+
component:
|
|
16
|
+
"A call button component that extends the Link component with a call icon and optional blinking dot.",
|
|
17
|
+
},
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
argTypes: {
|
|
21
|
+
showBlinkDot: {
|
|
22
|
+
control: { type: "boolean" },
|
|
23
|
+
description: "Show a blinking dot indicator",
|
|
24
|
+
},
|
|
25
|
+
buttonStyle: {
|
|
26
|
+
control: { type: "select" },
|
|
27
|
+
options: ["primary"],
|
|
28
|
+
description: "Visual style variant",
|
|
29
|
+
},
|
|
30
|
+
size: {
|
|
31
|
+
control: { type: "select" },
|
|
32
|
+
options: ["sm", "md", "lg"],
|
|
33
|
+
description: "Button size",
|
|
34
|
+
},
|
|
35
|
+
href: {
|
|
36
|
+
control: { type: "text" },
|
|
37
|
+
description: "Link destination",
|
|
38
|
+
},
|
|
39
|
+
children: {
|
|
40
|
+
control: { type: "text" },
|
|
41
|
+
description: "Button text content",
|
|
42
|
+
},
|
|
43
|
+
className: {
|
|
44
|
+
control: { type: "text" },
|
|
45
|
+
description: "Additional CSS classes",
|
|
46
|
+
},
|
|
47
|
+
},
|
|
48
|
+
args: {
|
|
49
|
+
href: "tel:+1234567890",
|
|
50
|
+
children: "Call Now",
|
|
51
|
+
showBlinkDot: false,
|
|
52
|
+
buttonStyle: "primary",
|
|
53
|
+
size: "md",
|
|
54
|
+
},
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
export default meta;
|
|
58
|
+
type Story = StoryObj<typeof meta>;
|
|
59
|
+
|
|
60
|
+
// All variants showcase - Real visual variants
|
|
61
|
+
export const AllVariants: Story = {
|
|
62
|
+
render: () => (
|
|
63
|
+
<div className="space-y-8">
|
|
64
|
+
<div>
|
|
65
|
+
<h3 className="mb-4 text-lg font-semibold">Style Variants</h3>
|
|
66
|
+
<div className="grid grid-cols-2 gap-4">
|
|
67
|
+
<div className="space-y-3">
|
|
68
|
+
<h4 className="text-sm font-medium text-gray-600">
|
|
69
|
+
Primary (Default)
|
|
70
|
+
</h4>
|
|
71
|
+
<CallButton href="tel:+1234567890" buttonStyle="primary">
|
|
72
|
+
Call Now
|
|
73
|
+
</CallButton>
|
|
74
|
+
</div>
|
|
75
|
+
</div>
|
|
76
|
+
</div>
|
|
77
|
+
|
|
78
|
+
<div>
|
|
79
|
+
<h3 className="mb-4 text-lg font-semibold">Size Variants</h3>
|
|
80
|
+
<div className="grid grid-cols-3 gap-4">
|
|
81
|
+
<div className="space-y-3">
|
|
82
|
+
<h4 className="text-sm font-medium text-gray-600">Small</h4>
|
|
83
|
+
<CallButton href="tel:+1234567890" size="sm">
|
|
84
|
+
Call
|
|
85
|
+
</CallButton>
|
|
86
|
+
</div>
|
|
87
|
+
<div className="space-y-3">
|
|
88
|
+
<h4 className="text-sm font-medium text-gray-600">
|
|
89
|
+
Medium (Default)
|
|
90
|
+
</h4>
|
|
91
|
+
<CallButton href="tel:+1234567890" size="md">
|
|
92
|
+
Call Now
|
|
93
|
+
</CallButton>
|
|
94
|
+
</div>
|
|
95
|
+
<div className="space-y-3">
|
|
96
|
+
<h4 className="text-sm font-medium text-gray-600">Large</h4>
|
|
97
|
+
<CallButton href="tel:+1234567890" size="lg">
|
|
98
|
+
Call Now
|
|
99
|
+
</CallButton>
|
|
100
|
+
</div>
|
|
101
|
+
</div>
|
|
102
|
+
</div>
|
|
103
|
+
|
|
104
|
+
<div>
|
|
105
|
+
<h3 className="mb-4 text-lg font-semibold">Blinking Dot Variant</h3>
|
|
106
|
+
<div className="space-y-3">
|
|
107
|
+
<CallButton
|
|
108
|
+
href="tel:+1234567890"
|
|
109
|
+
showBlinkDot={true}
|
|
110
|
+
buttonStyle="primary"
|
|
111
|
+
>
|
|
112
|
+
Emergency Hotline
|
|
113
|
+
</CallButton>
|
|
114
|
+
</div>
|
|
115
|
+
</div>
|
|
116
|
+
</div>
|
|
117
|
+
),
|
|
118
|
+
parameters: {
|
|
119
|
+
docs: {
|
|
120
|
+
description: {
|
|
121
|
+
story:
|
|
122
|
+
"Complete showcase of all CallButton visual variants: styles, sizes, and blinking dot.",
|
|
123
|
+
},
|
|
124
|
+
},
|
|
125
|
+
},
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
// Default call button
|
|
129
|
+
export const Default: Story = {
|
|
130
|
+
args: {
|
|
131
|
+
buttonStyle: "primary",
|
|
132
|
+
size: "md",
|
|
133
|
+
showBlinkDot: false,
|
|
134
|
+
},
|
|
135
|
+
parameters: {
|
|
136
|
+
docs: {
|
|
137
|
+
description: {
|
|
138
|
+
story: "Default CallButton with primary style and medium size.",
|
|
139
|
+
},
|
|
140
|
+
},
|
|
141
|
+
},
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
// Call button with blinking dot
|
|
145
|
+
export const WithBlinkDot: Story = {
|
|
146
|
+
args: {
|
|
147
|
+
showBlinkDot: true,
|
|
148
|
+
children: "Call Now",
|
|
149
|
+
buttonStyle: "primary",
|
|
150
|
+
size: "md",
|
|
151
|
+
},
|
|
152
|
+
parameters: {
|
|
153
|
+
docs: {
|
|
154
|
+
description: {
|
|
155
|
+
story:
|
|
156
|
+
"CallButton with blinking dot indicator for emergency or priority calls.",
|
|
157
|
+
},
|
|
158
|
+
},
|
|
159
|
+
},
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
// Style variations
|
|
163
|
+
export const StyleVariations: Story = {
|
|
164
|
+
render: () => (
|
|
165
|
+
<div className="space-y-4">
|
|
166
|
+
<div className="space-y-2">
|
|
167
|
+
<h4 className="text-sm font-medium text-gray-600">Primary Style</h4>
|
|
168
|
+
<CallButton href="tel:+1234567890" buttonStyle="primary">
|
|
169
|
+
Call Support
|
|
170
|
+
</CallButton>
|
|
171
|
+
</div>
|
|
172
|
+
</div>
|
|
173
|
+
),
|
|
174
|
+
parameters: {
|
|
175
|
+
docs: {
|
|
176
|
+
description: {
|
|
177
|
+
story:
|
|
178
|
+
"All four CallButton style variants: primary, secondary, outline, and ghost.",
|
|
179
|
+
},
|
|
180
|
+
},
|
|
181
|
+
},
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
// Size variations
|
|
185
|
+
export const SizeVariations: Story = {
|
|
186
|
+
render: () => (
|
|
187
|
+
<div className="space-y-4">
|
|
188
|
+
<div className="space-y-2">
|
|
189
|
+
<h4 className="text-sm font-medium text-gray-600">Small Size</h4>
|
|
190
|
+
<CallButton href="tel:+1234567890" size="sm">
|
|
191
|
+
Call
|
|
192
|
+
</CallButton>
|
|
193
|
+
</div>
|
|
194
|
+
<div className="space-y-2">
|
|
195
|
+
<h4 className="text-sm font-medium text-gray-600">
|
|
196
|
+
Medium Size (Default)
|
|
197
|
+
</h4>
|
|
198
|
+
<CallButton href="tel:+1234567890" size="md">
|
|
199
|
+
Call Now
|
|
200
|
+
</CallButton>
|
|
201
|
+
</div>
|
|
202
|
+
<div className="space-y-2">
|
|
203
|
+
<h4 className="text-sm font-medium text-gray-600">Large Size</h4>
|
|
204
|
+
<CallButton href="tel:+1234567890" size="lg">
|
|
205
|
+
Call Now
|
|
206
|
+
</CallButton>
|
|
207
|
+
</div>
|
|
208
|
+
</div>
|
|
209
|
+
),
|
|
210
|
+
parameters: {
|
|
211
|
+
docs: {
|
|
212
|
+
description: {
|
|
213
|
+
story: "All three CallButton size variants: small, medium, and large.",
|
|
214
|
+
},
|
|
215
|
+
},
|
|
216
|
+
},
|
|
217
|
+
};
|
|
218
|
+
|
|
219
|
+
// Interactive combinations
|
|
220
|
+
export const Interactive: Story = {
|
|
221
|
+
render: () => (
|
|
222
|
+
<div className="space-y-6">
|
|
223
|
+
<div>
|
|
224
|
+
<h3 className="mb-3 text-lg font-semibold">Emergency Contacts</h3>
|
|
225
|
+
<div className="space-y-3">
|
|
226
|
+
<CallButton
|
|
227
|
+
href="tel:+1234567890"
|
|
228
|
+
showBlinkDot={true}
|
|
229
|
+
buttonStyle="primary"
|
|
230
|
+
size="lg"
|
|
231
|
+
>
|
|
232
|
+
Emergency Hotline
|
|
233
|
+
</CallButton>
|
|
234
|
+
</div>
|
|
235
|
+
</div>
|
|
236
|
+
</div>
|
|
237
|
+
),
|
|
238
|
+
parameters: {
|
|
239
|
+
docs: {
|
|
240
|
+
description: {
|
|
241
|
+
story:
|
|
242
|
+
"Interactive examples showing combinations of CallButton variants for different use cases.",
|
|
243
|
+
},
|
|
244
|
+
},
|
|
245
|
+
},
|
|
246
|
+
};
|
|
247
|
+
|
|
248
|
+
// States and interactions
|
|
249
|
+
export const StatesAndInteractions: Story = {
|
|
250
|
+
render: () => (
|
|
251
|
+
<div className="space-y-6">
|
|
252
|
+
<div>
|
|
253
|
+
<h3 className="mb-3 text-lg font-semibold">Hover States</h3>
|
|
254
|
+
<p className="mb-2 text-sm text-gray-600">
|
|
255
|
+
Hover over the buttons to see interactive states
|
|
256
|
+
</p>
|
|
257
|
+
<div className="space-y-3">
|
|
258
|
+
<CallButton href="tel:+1234567890" showBlinkDot={true}>
|
|
259
|
+
Hover Me
|
|
260
|
+
</CallButton>
|
|
261
|
+
<CallButton href="tel:+1234567890">Hover Me Too</CallButton>
|
|
262
|
+
</div>
|
|
263
|
+
</div>
|
|
264
|
+
|
|
265
|
+
<div>
|
|
266
|
+
<h3 className="mb-3 text-lg font-semibold">Focus States</h3>
|
|
267
|
+
<p className="mb-2 text-sm text-gray-600">
|
|
268
|
+
Tab to focus and see keyboard navigation states
|
|
269
|
+
</p>
|
|
270
|
+
<div className="space-y-3">
|
|
271
|
+
<CallButton href="tel:+1234567890">Focus Me</CallButton>
|
|
272
|
+
<CallButton href="tel:+1234567890">Focus Me Too</CallButton>
|
|
273
|
+
</div>
|
|
274
|
+
</div>
|
|
275
|
+
</div>
|
|
276
|
+
),
|
|
277
|
+
parameters: {
|
|
278
|
+
docs: {
|
|
279
|
+
description: {
|
|
280
|
+
story:
|
|
281
|
+
"Demonstrates hover, focus, and interaction states of the CallButton component.",
|
|
282
|
+
},
|
|
283
|
+
},
|
|
284
|
+
},
|
|
285
|
+
};
|
|
286
|
+
|
|
287
|
+
// Accessibility showcase
|
|
288
|
+
export const Accessibility: Story = {
|
|
289
|
+
render: () => (
|
|
290
|
+
<div className="space-y-4">
|
|
291
|
+
<div>
|
|
292
|
+
<h3 className="mb-3 text-lg font-semibold">Screen Reader Friendly</h3>
|
|
293
|
+
<p className="mb-2 text-sm text-gray-600">
|
|
294
|
+
These buttons include proper ARIA labels and descriptions
|
|
295
|
+
</p>
|
|
296
|
+
<div className="space-y-3">
|
|
297
|
+
<CallButton
|
|
298
|
+
href="tel:+1234567890"
|
|
299
|
+
showBlinkDot={true}
|
|
300
|
+
aria-label="Call emergency support line"
|
|
301
|
+
aria-description="24/7 emergency hotline with priority response"
|
|
302
|
+
>
|
|
303
|
+
Emergency Support
|
|
304
|
+
</CallButton>
|
|
305
|
+
<CallButton
|
|
306
|
+
href="tel:+1234567890"
|
|
307
|
+
aria-label="Call customer service"
|
|
308
|
+
aria-description="General customer support and inquiries"
|
|
309
|
+
>
|
|
310
|
+
Customer Service
|
|
311
|
+
</CallButton>
|
|
312
|
+
</div>
|
|
313
|
+
</div>
|
|
314
|
+
</div>
|
|
315
|
+
),
|
|
316
|
+
parameters: {
|
|
317
|
+
docs: {
|
|
318
|
+
description: {
|
|
319
|
+
story:
|
|
320
|
+
"Accessibility features including ARIA labels and screen reader support.",
|
|
321
|
+
},
|
|
322
|
+
},
|
|
323
|
+
},
|
|
324
|
+
};
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { Link } from "@shared/components/link";
|
|
2
|
+
import { MaterialIcon } from "@shared/components/material-icon";
|
|
3
|
+
import type { IconProps } from "@shared/components/material-icon/types";
|
|
4
|
+
import type { CallButtonProps } from "@shared/components/call-button/types";
|
|
5
|
+
|
|
6
|
+
export const CallButton = (props: CallButtonProps) => {
|
|
7
|
+
const {
|
|
8
|
+
showBlinkDot = false,
|
|
9
|
+
buttonStyle = "primary",
|
|
10
|
+
size = "md",
|
|
11
|
+
children,
|
|
12
|
+
...rest
|
|
13
|
+
} = props;
|
|
14
|
+
|
|
15
|
+
const baseClasses =
|
|
16
|
+
"relative flex items-center gap-2 font-medium tracking-wide outline-offset-4 rounded-full";
|
|
17
|
+
|
|
18
|
+
const sizeClasses = {
|
|
19
|
+
sm: "h-6 text-xs pl-1 pr-2",
|
|
20
|
+
md: "h-8 text-sm pl-1 pr-2",
|
|
21
|
+
lg: "h-10 text-base pl-2 pr-3",
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
const styleClasses = {
|
|
25
|
+
primary:
|
|
26
|
+
"border-[0.727px] border-border text-text"
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
const iconSize: Record<string, IconProps["size"]> = {
|
|
30
|
+
sm: 20,
|
|
31
|
+
md: 20,
|
|
32
|
+
lg: 32,
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
const iconContainerSize = {
|
|
36
|
+
sm: "h-5 w-5",
|
|
37
|
+
md: "h-6 w-6",
|
|
38
|
+
lg: "h-8 w-8",
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
const dotSize = {
|
|
42
|
+
sm: "h-2 w-2",
|
|
43
|
+
md: "h-3 w-3",
|
|
44
|
+
lg: "h-4 w-4",
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
const dotPosition = {
|
|
48
|
+
sm: "-left-4",
|
|
49
|
+
md: "-left-6",
|
|
50
|
+
lg: "-left-8",
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
return (
|
|
54
|
+
<Link
|
|
55
|
+
{...rest}
|
|
56
|
+
className={`${baseClasses} ${sizeClasses[size]} ${styleClasses[buttonStyle]}`}
|
|
57
|
+
>
|
|
58
|
+
{showBlinkDot ? (
|
|
59
|
+
<span
|
|
60
|
+
className={`animate-blink pointer-events-none absolute ${dotPosition[size]} top-1/2 ${dotSize[size]} -translate-y-1/2 rounded-full bg-icon-brand`}
|
|
61
|
+
/>
|
|
62
|
+
) : null}
|
|
63
|
+
<span
|
|
64
|
+
className={`relative inline-flex ${iconContainerSize[size]} items-center justify-center rounded-full bg-icon-brand`}
|
|
65
|
+
>
|
|
66
|
+
<MaterialIcon
|
|
67
|
+
name="call"
|
|
68
|
+
fill={1}
|
|
69
|
+
size={iconSize[size]}
|
|
70
|
+
className="text-white"
|
|
71
|
+
/>
|
|
72
|
+
</span>
|
|
73
|
+
<span className="font-normal tracking-wide">{children}</span>
|
|
74
|
+
</Link>
|
|
75
|
+
);
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
CallButton.displayName = "CallButton";
|
|
79
|
+
|
|
80
|
+
export type { CallButtonProps };
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { ReactNode } from "react";
|
|
2
|
+
import { LinkProps } from "@shared/components/link/types";
|
|
3
|
+
|
|
4
|
+
export interface CallButtonProps extends Omit<LinkProps, "children"> {
|
|
5
|
+
showBlinkDot?: boolean;
|
|
6
|
+
buttonStyle?: "primary";
|
|
7
|
+
size?: "sm" | "md" | "lg";
|
|
8
|
+
children: ReactNode;
|
|
9
|
+
}
|
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
import { Checkbox } from "./index";
|
|
2
|
+
import { DocsPage } from "@shared/stories/DocsTemplate";
|
|
3
|
+
|
|
4
|
+
import type { Meta, StoryObj } from "@storybook/react";
|
|
5
|
+
import { useState } from "react";
|
|
6
|
+
|
|
7
|
+
const meta: Meta<typeof Checkbox> = {
|
|
8
|
+
title: "Components/Checkbox",
|
|
9
|
+
component: Checkbox,
|
|
10
|
+
tags: ["autodocs"],
|
|
11
|
+
parameters: {
|
|
12
|
+
layout: "centered",
|
|
13
|
+
docs: {
|
|
14
|
+
page: DocsPage,
|
|
15
|
+
description: {
|
|
16
|
+
component:
|
|
17
|
+
"A checkbox component with support for checked/unchecked states, labels, error states, and info icons.",
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
argTypes: {
|
|
22
|
+
checked: {
|
|
23
|
+
control: { type: "boolean" },
|
|
24
|
+
description: "Whether the checkbox is checked",
|
|
25
|
+
},
|
|
26
|
+
disabled: {
|
|
27
|
+
control: { type: "boolean" },
|
|
28
|
+
description: "Disable the checkbox",
|
|
29
|
+
},
|
|
30
|
+
label: {
|
|
31
|
+
control: { type: "text" },
|
|
32
|
+
description: "Label text for the checkbox",
|
|
33
|
+
},
|
|
34
|
+
error: {
|
|
35
|
+
control: { type: "boolean" },
|
|
36
|
+
description: "Show error state",
|
|
37
|
+
},
|
|
38
|
+
required: {
|
|
39
|
+
control: { type: "boolean" },
|
|
40
|
+
description: "Mark checkbox as required",
|
|
41
|
+
},
|
|
42
|
+
state: {
|
|
43
|
+
control: { type: "select" },
|
|
44
|
+
options: ["default", "focus", "disabled"],
|
|
45
|
+
description: "Visual state of the checkbox",
|
|
46
|
+
},
|
|
47
|
+
},
|
|
48
|
+
args: {
|
|
49
|
+
checked: false,
|
|
50
|
+
disabled: false,
|
|
51
|
+
label: "Checkbox Label",
|
|
52
|
+
error: false,
|
|
53
|
+
required: false,
|
|
54
|
+
},
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
export default meta;
|
|
58
|
+
type Story = StoryObj<typeof meta>;
|
|
59
|
+
|
|
60
|
+
// Default checkbox
|
|
61
|
+
export const Default: Story = {
|
|
62
|
+
args: {},
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
// Checked checkbox
|
|
66
|
+
export const Checked: Story = {
|
|
67
|
+
args: {
|
|
68
|
+
checked: true,
|
|
69
|
+
label: "Checked Checkbox",
|
|
70
|
+
},
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
// Unchecked checkbox
|
|
74
|
+
export const Unchecked: Story = {
|
|
75
|
+
args: {
|
|
76
|
+
checked: false,
|
|
77
|
+
label: "Unchecked Checkbox",
|
|
78
|
+
},
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
// With label
|
|
82
|
+
export const WithLabel: Story = {
|
|
83
|
+
args: {
|
|
84
|
+
checked: false,
|
|
85
|
+
label: "I agree to the terms and conditions",
|
|
86
|
+
},
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
// Disabled checkbox
|
|
90
|
+
export const Disabled: Story = {
|
|
91
|
+
args: {
|
|
92
|
+
checked: false,
|
|
93
|
+
label: "Disabled Checkbox",
|
|
94
|
+
disabled: true,
|
|
95
|
+
},
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
// Disabled checked
|
|
99
|
+
export const DisabledChecked: Story = {
|
|
100
|
+
args: {
|
|
101
|
+
checked: true,
|
|
102
|
+
label: "Disabled Checked Checkbox",
|
|
103
|
+
disabled: true,
|
|
104
|
+
},
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
// Error state
|
|
108
|
+
export const Error: Story = {
|
|
109
|
+
args: {
|
|
110
|
+
checked: false,
|
|
111
|
+
label: "Checkbox with Error",
|
|
112
|
+
error: true,
|
|
113
|
+
},
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
// Required checkbox
|
|
117
|
+
export const Required: Story = {
|
|
118
|
+
args: {
|
|
119
|
+
checked: false,
|
|
120
|
+
label: "Required Field",
|
|
121
|
+
required: true,
|
|
122
|
+
},
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
// Focus state
|
|
126
|
+
export const Focus: Story = {
|
|
127
|
+
args: {
|
|
128
|
+
checked: false,
|
|
129
|
+
label: "Focused Checkbox",
|
|
130
|
+
state: "focus",
|
|
131
|
+
},
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
// With info icon
|
|
135
|
+
export const WithInfoIcon: Story = {
|
|
136
|
+
args: {
|
|
137
|
+
checked: false,
|
|
138
|
+
label: "Checkbox with Info",
|
|
139
|
+
renderInfoIcon: {
|
|
140
|
+
onClick: () => alert("Info clicked"),
|
|
141
|
+
dataTestId: "info-icon",
|
|
142
|
+
},
|
|
143
|
+
},
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
// Interactive checkbox
|
|
147
|
+
export const Interactive: Story = {
|
|
148
|
+
render: () => {
|
|
149
|
+
const [checked, setChecked] = useState(false);
|
|
150
|
+
return (
|
|
151
|
+
<Checkbox
|
|
152
|
+
checked={checked}
|
|
153
|
+
onChange={(isChecked) => setChecked(isChecked)}
|
|
154
|
+
label="Click to toggle"
|
|
155
|
+
/>
|
|
156
|
+
);
|
|
157
|
+
},
|
|
158
|
+
parameters: {
|
|
159
|
+
docs: {
|
|
160
|
+
description: {
|
|
161
|
+
story: "Interactive checkbox that can be toggled by clicking.",
|
|
162
|
+
},
|
|
163
|
+
},
|
|
164
|
+
},
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
// Multiple checkboxes
|
|
168
|
+
export const Multiple: Story = {
|
|
169
|
+
render: () => {
|
|
170
|
+
const [items, setItems] = useState([
|
|
171
|
+
{ id: 1, label: "Option 1", checked: false },
|
|
172
|
+
{ id: 2, label: "Option 2", checked: true },
|
|
173
|
+
{ id: 3, label: "Option 3", checked: false },
|
|
174
|
+
]);
|
|
175
|
+
|
|
176
|
+
const handleChange = (id: number, isChecked: boolean) => {
|
|
177
|
+
setItems(
|
|
178
|
+
items.map((item) =>
|
|
179
|
+
item.id === id ? { ...item, checked: isChecked } : item
|
|
180
|
+
)
|
|
181
|
+
);
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
return (
|
|
185
|
+
<div className="space-y-3">
|
|
186
|
+
{items.map((item) => (
|
|
187
|
+
<Checkbox
|
|
188
|
+
key={item.id}
|
|
189
|
+
checked={item.checked}
|
|
190
|
+
onChange={(isChecked) => handleChange(item.id, isChecked)}
|
|
191
|
+
label={item.label}
|
|
192
|
+
/>
|
|
193
|
+
))}
|
|
194
|
+
</div>
|
|
195
|
+
);
|
|
196
|
+
},
|
|
197
|
+
parameters: {
|
|
198
|
+
docs: {
|
|
199
|
+
description: {
|
|
200
|
+
story: "Multiple checkboxes in a group.",
|
|
201
|
+
},
|
|
202
|
+
},
|
|
203
|
+
},
|
|
204
|
+
};
|
|
205
|
+
|
|
206
|
+
// All variants showcase
|
|
207
|
+
export const AllVariants: Story = {
|
|
208
|
+
render: () => (
|
|
209
|
+
<div className="space-y-6">
|
|
210
|
+
<div>
|
|
211
|
+
<h3 className="mb-3 text-lg font-semibold">States</h3>
|
|
212
|
+
<div className="space-y-3">
|
|
213
|
+
<Checkbox checked={false} label="Unchecked" />
|
|
214
|
+
<Checkbox checked={true} label="Checked" />
|
|
215
|
+
<Checkbox checked={false} label="Disabled" disabled />
|
|
216
|
+
<Checkbox checked={true} label="Disabled Checked" disabled />
|
|
217
|
+
</div>
|
|
218
|
+
</div>
|
|
219
|
+
<div>
|
|
220
|
+
<h3 className="mb-3 text-lg font-semibold">With Error</h3>
|
|
221
|
+
<Checkbox checked={false} label="Error State" error />
|
|
222
|
+
</div>
|
|
223
|
+
<div>
|
|
224
|
+
<h3 className="mb-3 text-lg font-semibold">Required</h3>
|
|
225
|
+
<Checkbox checked={false} label="Required Field" required />
|
|
226
|
+
</div>
|
|
227
|
+
<div>
|
|
228
|
+
<h3 className="mb-3 text-lg font-semibold">With Info Icon</h3>
|
|
229
|
+
<Checkbox
|
|
230
|
+
checked={false}
|
|
231
|
+
label="With Info Icon"
|
|
232
|
+
renderInfoIcon={{
|
|
233
|
+
onClick: () => {},
|
|
234
|
+
dataTestId: "info",
|
|
235
|
+
}}
|
|
236
|
+
/>
|
|
237
|
+
</div>
|
|
238
|
+
</div>
|
|
239
|
+
),
|
|
240
|
+
parameters: {
|
|
241
|
+
docs: {
|
|
242
|
+
description: {
|
|
243
|
+
story: "Showcase of all checkbox variants and states.",
|
|
244
|
+
},
|
|
245
|
+
},
|
|
246
|
+
},
|
|
247
|
+
};
|
|
248
|
+
|