@mzc-fe/design-system 0.0.1 → 0.0.2
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/components/accordion.d.ts +7 -0
- package/dist/components/alert-dialog.d.ts +14 -0
- package/dist/components/alert.d.ts +9 -0
- package/dist/components/aspect-ratio.d.ts +3 -0
- package/dist/components/avatar.d.ts +6 -0
- package/dist/components/badge.d.ts +9 -0
- package/dist/components/breadcrumb.d.ts +11 -0
- package/dist/components/button-group.d.ts +11 -0
- package/dist/components/button.d.ts +10 -0
- package/dist/components/calendar.d.ts +8 -0
- package/dist/components/card.d.ts +9 -0
- package/dist/components/carousel.d.ts +19 -0
- package/dist/components/chart.d.ts +40 -0
- package/dist/components/checkbox.d.ts +4 -0
- package/dist/components/collapsible.d.ts +5 -0
- package/dist/components/command.d.ts +18 -0
- package/dist/components/context-menu.d.ts +25 -0
- package/dist/components/dialog.d.ts +15 -0
- package/dist/components/drawer.d.ts +13 -0
- package/dist/components/dropdown-menu.d.ts +25 -0
- package/dist/components/empty.d.ts +11 -0
- package/dist/components/field.d.ts +24 -0
- package/dist/components/form.d.ts +24 -0
- package/dist/components/hover-card.d.ts +6 -0
- package/dist/components/input-group.d.ts +16 -0
- package/dist/components/input-otp.d.ts +11 -0
- package/dist/components/input.d.ts +3 -0
- package/dist/components/item.d.ts +23 -0
- package/dist/components/kbd.d.ts +3 -0
- package/dist/components/label.d.ts +4 -0
- package/dist/components/menubar.d.ts +26 -0
- package/dist/components/navigation-menu.d.ts +14 -0
- package/dist/components/pagination.d.ts +13 -0
- package/dist/components/popover.d.ts +7 -0
- package/dist/components/progress.d.ts +4 -0
- package/dist/components/radio-group.d.ts +5 -0
- package/dist/components/resizable.d.ts +8 -0
- package/dist/components/scroll-area.d.ts +5 -0
- package/dist/components/select.d.ts +15 -0
- package/dist/components/separator.d.ts +4 -0
- package/dist/components/sheet.d.ts +13 -0
- package/dist/components/sidebar.d.ts +69 -0
- package/dist/components/skeleton.d.ts +2 -0
- package/dist/components/slider.d.ts +4 -0
- package/dist/components/sonner.d.ts +3 -0
- package/dist/components/spinner.d.ts +2 -0
- package/dist/components/switch.d.ts +4 -0
- package/dist/components/table.d.ts +10 -0
- package/dist/components/tabs.d.ts +7 -0
- package/dist/components/textarea.d.ts +3 -0
- package/dist/components/toggle-group.d.ts +9 -0
- package/dist/components/toggle.d.ts +9 -0
- package/dist/components/tooltip.d.ts +7 -0
- package/dist/design-system.css +1 -0
- package/dist/design-system.es.js +30200 -0
- package/dist/design-system.umd.js +260 -0
- package/dist/foundations/ThemeProvider.d.ts +13 -0
- package/dist/hooks/use-mobile.d.ts +1 -0
- package/dist/index.d.ts +54 -0
- package/dist/lib/utils.d.ts +2 -0
- package/package.json +14 -1
- package/.husky/pre-push +0 -21
- package/.storybook/main.ts +0 -11
- package/.storybook/preview.tsx +0 -30
- package/.vscode/settings.json +0 -12
- package/.vscode/tailwind.json +0 -105
- package/bitbucket-pipelines.yml +0 -50
- package/components.json +0 -21
- package/eslint.config.js +0 -38
- package/src/components/accordion.stories.tsx +0 -258
- package/src/components/accordion.test.tsx +0 -390
- package/src/components/accordion.tsx +0 -64
- package/src/components/alert-dialog.stories.tsx +0 -213
- package/src/components/alert-dialog.test.tsx +0 -80
- package/src/components/alert-dialog.tsx +0 -155
- package/src/components/alert.stories.tsx +0 -84
- package/src/components/alert.test.tsx +0 -35
- package/src/components/alert.tsx +0 -66
- package/src/components/aspect-ratio.stories.tsx +0 -97
- package/src/components/aspect-ratio.test.tsx +0 -47
- package/src/components/aspect-ratio.tsx +0 -11
- package/src/components/avatar.stories.tsx +0 -76
- package/src/components/avatar.test.tsx +0 -50
- package/src/components/avatar.tsx +0 -51
- package/src/components/badge.stories.tsx +0 -64
- package/src/components/badge.test.tsx +0 -34
- package/src/components/badge.tsx +0 -46
- package/src/components/breadcrumb.stories.tsx +0 -86
- package/src/components/breadcrumb.test.tsx +0 -74
- package/src/components/breadcrumb.tsx +0 -109
- package/src/components/button-group.stories.tsx +0 -62
- package/src/components/button-group.tsx +0 -83
- package/src/components/button.stories.tsx +0 -118
- package/src/components/button.test.tsx +0 -64
- package/src/components/button.tsx +0 -62
- package/src/components/calendar.stories.tsx +0 -81
- package/src/components/calendar.tsx +0 -220
- package/src/components/card.stories.tsx +0 -110
- package/src/components/card.test.tsx +0 -56
- package/src/components/card.tsx +0 -92
- package/src/components/carousel.stories.tsx +0 -90
- package/src/components/carousel.tsx +0 -239
- package/src/components/chart.tsx +0 -357
- package/src/components/checkbox.stories.tsx +0 -108
- package/src/components/checkbox.test.tsx +0 -67
- package/src/components/checkbox.tsx +0 -32
- package/src/components/collapsible.stories.tsx +0 -106
- package/src/components/collapsible.test.tsx +0 -92
- package/src/components/collapsible.tsx +0 -31
- package/src/components/command.stories.tsx +0 -90
- package/src/components/command.tsx +0 -182
- package/src/components/context-menu.stories.tsx +0 -63
- package/src/components/context-menu.tsx +0 -252
- package/src/components/dialog.stories.tsx +0 -128
- package/src/components/dialog.tsx +0 -141
- package/src/components/drawer.stories.tsx +0 -104
- package/src/components/drawer.tsx +0 -135
- package/src/components/dropdown-menu.stories.tsx +0 -97
- package/src/components/dropdown-menu.tsx +0 -255
- package/src/components/empty.stories.tsx +0 -90
- package/src/components/empty.test.tsx +0 -55
- package/src/components/empty.tsx +0 -104
- package/src/components/field.tsx +0 -246
- package/src/components/form.tsx +0 -168
- package/src/components/hover-card.stories.tsx +0 -66
- package/src/components/hover-card.tsx +0 -44
- package/src/components/input-group.stories.tsx +0 -57
- package/src/components/input-group.test.tsx +0 -40
- package/src/components/input-group.tsx +0 -170
- package/src/components/input-otp.stories.tsx +0 -94
- package/src/components/input-otp.test.tsx +0 -60
- package/src/components/input-otp.tsx +0 -75
- package/src/components/input.stories.tsx +0 -94
- package/src/components/input.test.tsx +0 -53
- package/src/components/input.tsx +0 -21
- package/src/components/item.tsx +0 -193
- package/src/components/kbd.stories.tsx +0 -100
- package/src/components/kbd.test.tsx +0 -28
- package/src/components/kbd.tsx +0 -28
- package/src/components/label.stories.tsx +0 -48
- package/src/components/label.test.tsx +0 -28
- package/src/components/label.tsx +0 -24
- package/src/components/menubar.tsx +0 -274
- package/src/components/navigation-menu.tsx +0 -168
- package/src/components/pagination.stories.tsx +0 -107
- package/src/components/pagination.tsx +0 -127
- package/src/components/popover.stories.tsx +0 -102
- package/src/components/popover.tsx +0 -48
- package/src/components/progress.stories.tsx +0 -76
- package/src/components/progress.test.tsx +0 -36
- package/src/components/progress.tsx +0 -29
- package/src/components/radio-group.stories.tsx +0 -73
- package/src/components/radio-group.test.tsx +0 -74
- package/src/components/radio-group.tsx +0 -45
- package/src/components/resizable.stories.tsx +0 -120
- package/src/components/resizable.tsx +0 -54
- package/src/components/scroll-area.stories.tsx +0 -64
- package/src/components/scroll-area.test.tsx +0 -46
- package/src/components/scroll-area.tsx +0 -58
- package/src/components/select.stories.tsx +0 -111
- package/src/components/select.test.tsx +0 -90
- package/src/components/select.tsx +0 -188
- package/src/components/separator.stories.tsx +0 -76
- package/src/components/separator.test.tsx +0 -24
- package/src/components/separator.tsx +0 -28
- package/src/components/sheet.stories.tsx +0 -122
- package/src/components/sheet.tsx +0 -137
- package/src/components/sidebar.tsx +0 -726
- package/src/components/skeleton.stories.tsx +0 -53
- package/src/components/skeleton.test.tsx +0 -24
- package/src/components/skeleton.tsx +0 -13
- package/src/components/slider.stories.tsx +0 -97
- package/src/components/slider.test.tsx +0 -49
- package/src/components/slider.tsx +0 -63
- package/src/components/sonner.stories.tsx +0 -96
- package/src/components/sonner.tsx +0 -38
- package/src/components/spinner.stories.tsx +0 -54
- package/src/components/spinner.test.tsx +0 -30
- package/src/components/spinner.tsx +0 -16
- package/src/components/switch.stories.tsx +0 -108
- package/src/components/switch.test.tsx +0 -62
- package/src/components/switch.tsx +0 -31
- package/src/components/table.stories.tsx +0 -139
- package/src/components/table.test.tsx +0 -85
- package/src/components/table.tsx +0 -114
- package/src/components/tabs.stories.tsx +0 -99
- package/src/components/tabs.test.tsx +0 -64
- package/src/components/tabs.tsx +0 -66
- package/src/components/textarea.stories.tsx +0 -89
- package/src/components/textarea.test.tsx +0 -53
- package/src/components/textarea.tsx +0 -18
- package/src/components/toggle-group.stories.tsx +0 -108
- package/src/components/toggle-group.test.tsx +0 -66
- package/src/components/toggle-group.tsx +0 -81
- package/src/components/toggle.stories.tsx +0 -98
- package/src/components/toggle.test.tsx +0 -42
- package/src/components/toggle.tsx +0 -45
- package/src/components/tooltip.stories.tsx +0 -111
- package/src/components/tooltip.tsx +0 -61
- package/src/foundations/README.md +0 -141
- package/src/foundations/ThemeProvider.tsx +0 -77
- package/src/foundations/color.css +0 -232
- package/src/foundations/color.stories.tsx +0 -719
- package/src/foundations/palette.css +0 -249
- package/src/foundations/spacing.css +0 -8
- package/src/foundations/typography.css +0 -143
- package/src/foundations/typography.stories.tsx +0 -17
- package/src/hooks/use-mobile.ts +0 -19
- package/src/index.css +0 -176
- package/src/index.ts +0 -336
- package/src/lib/utils.ts +0 -6
- package/src/test/setup.ts +0 -8
- package/src/vite-env.d.ts +0 -1
- package/tsconfig.app.json +0 -33
- package/tsconfig.json +0 -13
- package/tsconfig.node.json +0 -25
- package/vite.config.ts +0 -30
- package/vitest.config.ts +0 -25
- /package/{public → dist}/vite.svg +0 -0
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from "vitest";
|
|
2
|
-
import { render } from "@testing-library/react";
|
|
3
|
-
import {
|
|
4
|
-
InputGroup,
|
|
5
|
-
InputGroupAddon,
|
|
6
|
-
InputGroupInput,
|
|
7
|
-
} from "./input-group";
|
|
8
|
-
|
|
9
|
-
describe("InputGroup", () => {
|
|
10
|
-
it("should render input group", () => {
|
|
11
|
-
const { container } = render(
|
|
12
|
-
<InputGroup>
|
|
13
|
-
<InputGroupInput placeholder="Enter text" />
|
|
14
|
-
</InputGroup>
|
|
15
|
-
);
|
|
16
|
-
const group = container.querySelector('[data-slot="input-group"]');
|
|
17
|
-
expect(group).toBeInTheDocument();
|
|
18
|
-
});
|
|
19
|
-
|
|
20
|
-
it("should render input group with addon", () => {
|
|
21
|
-
const { getByText } = render(
|
|
22
|
-
<InputGroup>
|
|
23
|
-
<InputGroupAddon align="inline-start">$</InputGroupAddon>
|
|
24
|
-
<InputGroupInput placeholder="0.00" />
|
|
25
|
-
</InputGroup>
|
|
26
|
-
);
|
|
27
|
-
expect(getByText("$")).toBeInTheDocument();
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
it("should render input group input", () => {
|
|
31
|
-
const { container } = render(
|
|
32
|
-
<InputGroup>
|
|
33
|
-
<InputGroupInput placeholder="Enter text" />
|
|
34
|
-
</InputGroup>
|
|
35
|
-
);
|
|
36
|
-
const input = container.querySelector('[data-slot="input-group-control"]');
|
|
37
|
-
expect(input).toBeInTheDocument();
|
|
38
|
-
});
|
|
39
|
-
});
|
|
40
|
-
|
|
@@ -1,170 +0,0 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
|
|
3
|
-
import * as React from "react";
|
|
4
|
-
import { cva, type VariantProps } from "class-variance-authority";
|
|
5
|
-
|
|
6
|
-
import { cn } from "@/lib/utils";
|
|
7
|
-
import { Button } from "@/components/button";
|
|
8
|
-
import { Input } from "@/components/input";
|
|
9
|
-
import { Textarea } from "@/components/textarea";
|
|
10
|
-
|
|
11
|
-
function InputGroup({ className, ...props }: React.ComponentProps<"div">) {
|
|
12
|
-
return (
|
|
13
|
-
<div
|
|
14
|
-
data-slot="input-group"
|
|
15
|
-
role="group"
|
|
16
|
-
className={cn(
|
|
17
|
-
"group/input-group border-input dark:bg-input/30 relative flex w-full items-center rounded-md border shadow-xs transition-[color,box-shadow] outline-none",
|
|
18
|
-
"h-9 min-w-0 has-[>textarea]:h-auto",
|
|
19
|
-
|
|
20
|
-
// Variants based on alignment.
|
|
21
|
-
"has-[>[data-align=inline-start]]:[&>input]:pl-2",
|
|
22
|
-
"has-[>[data-align=inline-end]]:[&>input]:pr-2",
|
|
23
|
-
"has-[>[data-align=block-start]]:h-auto has-[>[data-align=block-start]]:flex-col has-[>[data-align=block-start]]:[&>input]:pb-3",
|
|
24
|
-
"has-[>[data-align=block-end]]:h-auto has-[>[data-align=block-end]]:flex-col has-[>[data-align=block-end]]:[&>input]:pt-3",
|
|
25
|
-
|
|
26
|
-
// Focus state.
|
|
27
|
-
"has-[[data-slot=input-group-control]:focus-visible]:border-ring has-[[data-slot=input-group-control]:focus-visible]:ring-ring/50 has-[[data-slot=input-group-control]:focus-visible]:ring-[3px]",
|
|
28
|
-
|
|
29
|
-
// Error state.
|
|
30
|
-
"has-[[data-slot][aria-invalid=true]]:ring-destructive/20 has-[[data-slot][aria-invalid=true]]:border-destructive dark:has-[[data-slot][aria-invalid=true]]:ring-destructive/40",
|
|
31
|
-
|
|
32
|
-
className
|
|
33
|
-
)}
|
|
34
|
-
{...props}
|
|
35
|
-
/>
|
|
36
|
-
);
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
const inputGroupAddonVariants = cva(
|
|
40
|
-
"text-muted-foreground flex h-auto cursor-text items-center justify-center gap-2 py-1.5 text-sm font-medium select-none [&>svg:not([class*='size-'])]:size-4 [&>kbd]:rounded-[calc(var(--radius)-5px)] group-data-[disabled=true]/input-group:opacity-50",
|
|
41
|
-
{
|
|
42
|
-
variants: {
|
|
43
|
-
align: {
|
|
44
|
-
"inline-start":
|
|
45
|
-
"order-first pl-3 has-[>button]:ml-[-0.45rem] has-[>kbd]:ml-[-0.35rem]",
|
|
46
|
-
"inline-end":
|
|
47
|
-
"order-last pr-3 has-[>button]:mr-[-0.45rem] has-[>kbd]:mr-[-0.35rem]",
|
|
48
|
-
"block-start":
|
|
49
|
-
"order-first w-full justify-start px-3 pt-3 [.border-b]:pb-3 group-has-[>input]/input-group:pt-2.5",
|
|
50
|
-
"block-end":
|
|
51
|
-
"order-last w-full justify-start px-3 pb-3 [.border-t]:pt-3 group-has-[>input]/input-group:pb-2.5",
|
|
52
|
-
},
|
|
53
|
-
},
|
|
54
|
-
defaultVariants: {
|
|
55
|
-
align: "inline-start",
|
|
56
|
-
},
|
|
57
|
-
}
|
|
58
|
-
);
|
|
59
|
-
|
|
60
|
-
function InputGroupAddon({
|
|
61
|
-
className,
|
|
62
|
-
align = "inline-start",
|
|
63
|
-
...props
|
|
64
|
-
}: React.ComponentProps<"div"> & VariantProps<typeof inputGroupAddonVariants>) {
|
|
65
|
-
return (
|
|
66
|
-
<div
|
|
67
|
-
role="group"
|
|
68
|
-
data-slot="input-group-addon"
|
|
69
|
-
data-align={align}
|
|
70
|
-
className={cn(inputGroupAddonVariants({ align }), className)}
|
|
71
|
-
onClick={(e) => {
|
|
72
|
-
if ((e.target as HTMLElement).closest("button")) {
|
|
73
|
-
return;
|
|
74
|
-
}
|
|
75
|
-
e.currentTarget.parentElement?.querySelector("input")?.focus();
|
|
76
|
-
}}
|
|
77
|
-
{...props}
|
|
78
|
-
/>
|
|
79
|
-
);
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
const inputGroupButtonVariants = cva(
|
|
83
|
-
"text-sm shadow-none flex gap-2 items-center",
|
|
84
|
-
{
|
|
85
|
-
variants: {
|
|
86
|
-
size: {
|
|
87
|
-
xs: "h-6 gap-1 px-2 rounded-[calc(var(--radius)-5px)] [&>svg:not([class*='size-'])]:size-3.5 has-[>svg]:px-2",
|
|
88
|
-
sm: "h-8 px-2.5 gap-1.5 rounded-md has-[>svg]:px-2.5",
|
|
89
|
-
"icon-xs":
|
|
90
|
-
"size-6 rounded-[calc(var(--radius)-5px)] p-0 has-[>svg]:p-0",
|
|
91
|
-
"icon-sm": "size-8 p-0 has-[>svg]:p-0",
|
|
92
|
-
},
|
|
93
|
-
},
|
|
94
|
-
defaultVariants: {
|
|
95
|
-
size: "xs",
|
|
96
|
-
},
|
|
97
|
-
}
|
|
98
|
-
);
|
|
99
|
-
|
|
100
|
-
function InputGroupButton({
|
|
101
|
-
className,
|
|
102
|
-
type = "button",
|
|
103
|
-
variant = "ghost",
|
|
104
|
-
size = "xs",
|
|
105
|
-
...props
|
|
106
|
-
}: Omit<React.ComponentProps<typeof Button>, "size"> &
|
|
107
|
-
VariantProps<typeof inputGroupButtonVariants>) {
|
|
108
|
-
return (
|
|
109
|
-
<Button
|
|
110
|
-
type={type}
|
|
111
|
-
data-size={size}
|
|
112
|
-
variant={variant}
|
|
113
|
-
className={cn(inputGroupButtonVariants({ size }), className)}
|
|
114
|
-
{...props}
|
|
115
|
-
/>
|
|
116
|
-
);
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
function InputGroupText({ className, ...props }: React.ComponentProps<"span">) {
|
|
120
|
-
return (
|
|
121
|
-
<span
|
|
122
|
-
className={cn(
|
|
123
|
-
"text-muted-foreground flex items-center gap-2 text-sm [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4",
|
|
124
|
-
className
|
|
125
|
-
)}
|
|
126
|
-
{...props}
|
|
127
|
-
/>
|
|
128
|
-
);
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
function InputGroupInput({
|
|
132
|
-
className,
|
|
133
|
-
...props
|
|
134
|
-
}: React.ComponentProps<"input">) {
|
|
135
|
-
return (
|
|
136
|
-
<Input
|
|
137
|
-
data-slot="input-group-control"
|
|
138
|
-
className={cn(
|
|
139
|
-
"flex-1 rounded-none border-0 bg-transparent shadow-none focus-visible:ring-0 dark:bg-transparent",
|
|
140
|
-
className
|
|
141
|
-
)}
|
|
142
|
-
{...props}
|
|
143
|
-
/>
|
|
144
|
-
);
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
function InputGroupTextarea({
|
|
148
|
-
className,
|
|
149
|
-
...props
|
|
150
|
-
}: React.ComponentProps<"textarea">) {
|
|
151
|
-
return (
|
|
152
|
-
<Textarea
|
|
153
|
-
data-slot="input-group-control"
|
|
154
|
-
className={cn(
|
|
155
|
-
"flex-1 resize-none rounded-none border-0 bg-transparent py-3 shadow-none focus-visible:ring-0 dark:bg-transparent",
|
|
156
|
-
className
|
|
157
|
-
)}
|
|
158
|
-
{...props}
|
|
159
|
-
/>
|
|
160
|
-
);
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
export {
|
|
164
|
-
InputGroup,
|
|
165
|
-
InputGroupAddon,
|
|
166
|
-
InputGroupButton,
|
|
167
|
-
InputGroupText,
|
|
168
|
-
InputGroupInput,
|
|
169
|
-
InputGroupTextarea,
|
|
170
|
-
};
|
|
@@ -1,94 +0,0 @@
|
|
|
1
|
-
import type { Meta, StoryObj } from "@storybook/react-vite";
|
|
2
|
-
import { InputOTP, InputOTPGroup, InputOTPSlot } from "./input-otp";
|
|
3
|
-
|
|
4
|
-
const meta = {
|
|
5
|
-
title: "Components/InputOTP",
|
|
6
|
-
component: InputOTP,
|
|
7
|
-
parameters: {
|
|
8
|
-
layout: "padded",
|
|
9
|
-
},
|
|
10
|
-
tags: ["autodocs"],
|
|
11
|
-
argTypes: {
|
|
12
|
-
maxLength: {
|
|
13
|
-
control: "number",
|
|
14
|
-
description: "Maximum number of characters.",
|
|
15
|
-
},
|
|
16
|
-
children: {
|
|
17
|
-
table: {
|
|
18
|
-
disable: true,
|
|
19
|
-
},
|
|
20
|
-
},
|
|
21
|
-
},
|
|
22
|
-
} satisfies Meta<typeof InputOTP>;
|
|
23
|
-
|
|
24
|
-
export default meta;
|
|
25
|
-
type Story = StoryObj<typeof meta>;
|
|
26
|
-
|
|
27
|
-
export const Default: Story = {
|
|
28
|
-
args: {
|
|
29
|
-
maxLength: 6,
|
|
30
|
-
children: <></>,
|
|
31
|
-
},
|
|
32
|
-
render: (args) => (
|
|
33
|
-
<InputOTP maxLength={args.maxLength}>
|
|
34
|
-
<InputOTPGroup>
|
|
35
|
-
{new Array(args.maxLength).fill(0).map((_, idx) => (
|
|
36
|
-
<InputOTPSlot index={idx} />
|
|
37
|
-
))}
|
|
38
|
-
</InputOTPGroup>
|
|
39
|
-
</InputOTP>
|
|
40
|
-
),
|
|
41
|
-
};
|
|
42
|
-
|
|
43
|
-
export const FourDigits: Story = {
|
|
44
|
-
args: {
|
|
45
|
-
maxLength: 4,
|
|
46
|
-
children: <></>,
|
|
47
|
-
},
|
|
48
|
-
argTypes: {
|
|
49
|
-
maxLength: {
|
|
50
|
-
table: {
|
|
51
|
-
disable: true,
|
|
52
|
-
},
|
|
53
|
-
},
|
|
54
|
-
},
|
|
55
|
-
render: () => (
|
|
56
|
-
<InputOTP maxLength={4}>
|
|
57
|
-
<InputOTPGroup>
|
|
58
|
-
<InputOTPSlot index={0} />
|
|
59
|
-
<InputOTPSlot index={1} />
|
|
60
|
-
<InputOTPSlot index={2} />
|
|
61
|
-
<InputOTPSlot index={3} />
|
|
62
|
-
</InputOTPGroup>
|
|
63
|
-
</InputOTP>
|
|
64
|
-
),
|
|
65
|
-
};
|
|
66
|
-
|
|
67
|
-
export const WithSeparator: Story = {
|
|
68
|
-
args: {
|
|
69
|
-
maxLength: 6,
|
|
70
|
-
children: <></>,
|
|
71
|
-
},
|
|
72
|
-
argTypes: {
|
|
73
|
-
maxLength: {
|
|
74
|
-
table: {
|
|
75
|
-
disable: true,
|
|
76
|
-
},
|
|
77
|
-
},
|
|
78
|
-
},
|
|
79
|
-
render: () => (
|
|
80
|
-
<InputOTP maxLength={6}>
|
|
81
|
-
<InputOTPGroup>
|
|
82
|
-
<InputOTPSlot index={0} />
|
|
83
|
-
<InputOTPSlot index={1} />
|
|
84
|
-
<InputOTPSlot index={2} />
|
|
85
|
-
</InputOTPGroup>
|
|
86
|
-
<div className="w-2" />
|
|
87
|
-
<InputOTPGroup>
|
|
88
|
-
<InputOTPSlot index={3} />
|
|
89
|
-
<InputOTPSlot index={4} />
|
|
90
|
-
<InputOTPSlot index={5} />
|
|
91
|
-
</InputOTPGroup>
|
|
92
|
-
</InputOTP>
|
|
93
|
-
),
|
|
94
|
-
};
|
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, vi } from "vitest";
|
|
2
|
-
import { render } from "@testing-library/react";
|
|
3
|
-
import {
|
|
4
|
-
InputOTP,
|
|
5
|
-
InputOTPGroup,
|
|
6
|
-
InputOTPSlot,
|
|
7
|
-
} from "./input-otp";
|
|
8
|
-
|
|
9
|
-
describe("InputOTP", () => {
|
|
10
|
-
it("should render input OTP", () => {
|
|
11
|
-
const { container } = render(
|
|
12
|
-
<InputOTP maxLength={6}>
|
|
13
|
-
<InputOTPGroup>
|
|
14
|
-
<InputOTPSlot index={0} />
|
|
15
|
-
</InputOTPGroup>
|
|
16
|
-
</InputOTP>
|
|
17
|
-
);
|
|
18
|
-
const inputOTP = container.querySelector('[data-slot="input-otp"]');
|
|
19
|
-
expect(inputOTP).toBeInTheDocument();
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
it("should render input OTP group", () => {
|
|
23
|
-
const { container } = render(
|
|
24
|
-
<InputOTP maxLength={6}>
|
|
25
|
-
<InputOTPGroup>
|
|
26
|
-
<InputOTPSlot index={0} />
|
|
27
|
-
</InputOTPGroup>
|
|
28
|
-
</InputOTP>
|
|
29
|
-
);
|
|
30
|
-
const group = container.querySelector('[data-slot="input-otp-group"]');
|
|
31
|
-
expect(group).toBeInTheDocument();
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
it("should render input OTP slots", () => {
|
|
35
|
-
const { container } = render(
|
|
36
|
-
<InputOTP maxLength={6}>
|
|
37
|
-
<InputOTPGroup>
|
|
38
|
-
<InputOTPSlot index={0} />
|
|
39
|
-
<InputOTPSlot index={1} />
|
|
40
|
-
</InputOTPGroup>
|
|
41
|
-
</InputOTP>
|
|
42
|
-
);
|
|
43
|
-
const slots = container.querySelectorAll('[data-slot="input-otp-slot"]');
|
|
44
|
-
expect(slots.length).toBe(2);
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
it("should handle value changes", () => {
|
|
48
|
-
const handleChange = vi.fn();
|
|
49
|
-
const { container } = render(
|
|
50
|
-
<InputOTP maxLength={6} onChange={handleChange}>
|
|
51
|
-
<InputOTPGroup>
|
|
52
|
-
<InputOTPSlot index={0} />
|
|
53
|
-
</InputOTPGroup>
|
|
54
|
-
</InputOTP>
|
|
55
|
-
);
|
|
56
|
-
const input = container.querySelector('input');
|
|
57
|
-
expect(input).toBeInTheDocument();
|
|
58
|
-
});
|
|
59
|
-
});
|
|
60
|
-
|
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
import * as React from "react";
|
|
2
|
-
import { OTPInput, OTPInputContext } from "input-otp";
|
|
3
|
-
import { MinusIcon } from "lucide-react";
|
|
4
|
-
|
|
5
|
-
import { cn } from "@/lib/utils";
|
|
6
|
-
|
|
7
|
-
function InputOTP({
|
|
8
|
-
className,
|
|
9
|
-
containerClassName,
|
|
10
|
-
...props
|
|
11
|
-
}: React.ComponentProps<typeof OTPInput> & {
|
|
12
|
-
containerClassName?: string;
|
|
13
|
-
}) {
|
|
14
|
-
return (
|
|
15
|
-
<OTPInput
|
|
16
|
-
data-slot="input-otp"
|
|
17
|
-
containerClassName={cn(
|
|
18
|
-
"flex items-center gap-2 has-disabled:opacity-50",
|
|
19
|
-
containerClassName
|
|
20
|
-
)}
|
|
21
|
-
className={cn("disabled:cursor-not-allowed", className)}
|
|
22
|
-
{...props}
|
|
23
|
-
/>
|
|
24
|
-
);
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
function InputOTPGroup({ className, ...props }: React.ComponentProps<"div">) {
|
|
28
|
-
return (
|
|
29
|
-
<div
|
|
30
|
-
data-slot="input-otp-group"
|
|
31
|
-
className={cn("flex items-center", className)}
|
|
32
|
-
{...props}
|
|
33
|
-
/>
|
|
34
|
-
);
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
function InputOTPSlot({
|
|
38
|
-
index,
|
|
39
|
-
className,
|
|
40
|
-
...props
|
|
41
|
-
}: React.ComponentProps<"div"> & {
|
|
42
|
-
index: number;
|
|
43
|
-
}) {
|
|
44
|
-
const inputOTPContext = React.useContext(OTPInputContext);
|
|
45
|
-
const { char, hasFakeCaret, isActive } = inputOTPContext?.slots[index] ?? {};
|
|
46
|
-
|
|
47
|
-
return (
|
|
48
|
-
<div
|
|
49
|
-
data-slot="input-otp-slot"
|
|
50
|
-
data-active={isActive}
|
|
51
|
-
className={cn(
|
|
52
|
-
"data-[active=true]:border-ring data-[active=true]:ring-ring/50 data-[active=true]:aria-invalid:ring-destructive/20 dark:data-[active=true]:aria-invalid:ring-destructive/40 aria-invalid:border-destructive data-[active=true]:aria-invalid:border-destructive dark:bg-input/30 border-input relative flex h-9 w-9 items-center justify-center border-y border-r text-sm shadow-xs transition-all outline-none first:rounded-l-md first:border-l last:rounded-r-md data-[active=true]:z-10 data-[active=true]:ring-[3px]",
|
|
53
|
-
className
|
|
54
|
-
)}
|
|
55
|
-
{...props}
|
|
56
|
-
>
|
|
57
|
-
{char}
|
|
58
|
-
{hasFakeCaret && (
|
|
59
|
-
<div className="pointer-events-none absolute inset-0 flex items-center justify-center">
|
|
60
|
-
<div className="animate-caret-blink bg-foreground h-4 w-px duration-1000" />
|
|
61
|
-
</div>
|
|
62
|
-
)}
|
|
63
|
-
</div>
|
|
64
|
-
);
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
function InputOTPSeparator({ ...props }: React.ComponentProps<"div">) {
|
|
68
|
-
return (
|
|
69
|
-
<div data-slot="input-otp-separator" role="separator" {...props}>
|
|
70
|
-
<MinusIcon />
|
|
71
|
-
</div>
|
|
72
|
-
);
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
export { InputOTP, InputOTPGroup, InputOTPSlot, InputOTPSeparator };
|
|
@@ -1,94 +0,0 @@
|
|
|
1
|
-
import type { Meta, StoryObj } from "@storybook/react-vite";
|
|
2
|
-
import { Input } from "./input";
|
|
3
|
-
import { Label } from "./label";
|
|
4
|
-
|
|
5
|
-
const meta = {
|
|
6
|
-
title: "Components/Input",
|
|
7
|
-
component: Input,
|
|
8
|
-
parameters: {
|
|
9
|
-
layout: "padded",
|
|
10
|
-
},
|
|
11
|
-
tags: ["autodocs"],
|
|
12
|
-
argTypes: {
|
|
13
|
-
type: {
|
|
14
|
-
control: "select",
|
|
15
|
-
options: ["text", "email", "password", "number", "tel", "url"],
|
|
16
|
-
description: "The input type.",
|
|
17
|
-
},
|
|
18
|
-
disabled: {
|
|
19
|
-
control: "boolean",
|
|
20
|
-
description: "Whether the input is disabled.",
|
|
21
|
-
},
|
|
22
|
-
placeholder: {
|
|
23
|
-
control: "text",
|
|
24
|
-
description: "Placeholder text.",
|
|
25
|
-
},
|
|
26
|
-
},
|
|
27
|
-
} satisfies Meta<typeof Input>;
|
|
28
|
-
|
|
29
|
-
export default meta;
|
|
30
|
-
type Story = StoryObj<typeof meta>;
|
|
31
|
-
|
|
32
|
-
export const Default: Story = {
|
|
33
|
-
args: {
|
|
34
|
-
type: "text",
|
|
35
|
-
placeholder: "Enter text...",
|
|
36
|
-
},
|
|
37
|
-
};
|
|
38
|
-
|
|
39
|
-
export const WithLabel: Story = {
|
|
40
|
-
render: () => (
|
|
41
|
-
<div className="space-y-2 w-[350px]">
|
|
42
|
-
<Label htmlFor="email">Email</Label>
|
|
43
|
-
<Input id="email" type="email" placeholder="name@example.com" />
|
|
44
|
-
</div>
|
|
45
|
-
),
|
|
46
|
-
};
|
|
47
|
-
|
|
48
|
-
export const Types: Story = {
|
|
49
|
-
render: () => (
|
|
50
|
-
<div className="space-y-4 w-[350px]">
|
|
51
|
-
<div className="space-y-2">
|
|
52
|
-
<Label htmlFor="text">Text</Label>
|
|
53
|
-
<Input id="text" type="text" placeholder="Enter text" />
|
|
54
|
-
</div>
|
|
55
|
-
<div className="space-y-2">
|
|
56
|
-
<Label htmlFor="email">Email</Label>
|
|
57
|
-
<Input id="email" type="email" placeholder="name@example.com" />
|
|
58
|
-
</div>
|
|
59
|
-
<div className="space-y-2">
|
|
60
|
-
<Label htmlFor="password">Password</Label>
|
|
61
|
-
<Input id="password" type="password" placeholder="••••••••" />
|
|
62
|
-
</div>
|
|
63
|
-
<div className="space-y-2">
|
|
64
|
-
<Label htmlFor="number">Number</Label>
|
|
65
|
-
<Input id="number" type="number" placeholder="123" />
|
|
66
|
-
</div>
|
|
67
|
-
</div>
|
|
68
|
-
),
|
|
69
|
-
};
|
|
70
|
-
|
|
71
|
-
export const Disabled: Story = {
|
|
72
|
-
render: () => (
|
|
73
|
-
<div className="space-y-4 w-[350px]">
|
|
74
|
-
<Input disabled placeholder="Disabled input" />
|
|
75
|
-
<Input disabled value="Disabled with value" />
|
|
76
|
-
</div>
|
|
77
|
-
),
|
|
78
|
-
};
|
|
79
|
-
|
|
80
|
-
export const WithError: Story = {
|
|
81
|
-
render: () => (
|
|
82
|
-
<div className="space-y-2 w-[350px]">
|
|
83
|
-
<Label htmlFor="error">Email</Label>
|
|
84
|
-
<Input
|
|
85
|
-
id="error"
|
|
86
|
-
type="email"
|
|
87
|
-
placeholder="name@example.com"
|
|
88
|
-
aria-invalid="true"
|
|
89
|
-
/>
|
|
90
|
-
<p className="text-sm text-destructive">Please enter a valid email.</p>
|
|
91
|
-
</div>
|
|
92
|
-
),
|
|
93
|
-
};
|
|
94
|
-
|
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, vi } from "vitest";
|
|
2
|
-
import { render } from "@testing-library/react";
|
|
3
|
-
import userEvent from "@testing-library/user-event";
|
|
4
|
-
import { Input } from "./input";
|
|
5
|
-
|
|
6
|
-
describe("Input", () => {
|
|
7
|
-
it("should render input element", () => {
|
|
8
|
-
const { container } = render(<Input />);
|
|
9
|
-
const input = container.querySelector('[data-slot="input"]');
|
|
10
|
-
expect(input).toBeInTheDocument();
|
|
11
|
-
});
|
|
12
|
-
|
|
13
|
-
it("should display placeholder text", () => {
|
|
14
|
-
const { getByPlaceholderText } = render(
|
|
15
|
-
<Input placeholder="Enter text..." />
|
|
16
|
-
);
|
|
17
|
-
expect(getByPlaceholderText("Enter text...")).toBeInTheDocument();
|
|
18
|
-
});
|
|
19
|
-
|
|
20
|
-
it("should accept and display value", () => {
|
|
21
|
-
const { container } = render(<Input value="test value" readOnly />);
|
|
22
|
-
const input = container.querySelector('[data-slot="input"]') as HTMLInputElement;
|
|
23
|
-
expect(input.value).toBe("test value");
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
it("should be disabled when disabled prop is true", () => {
|
|
27
|
-
const { container } = render(<Input disabled />);
|
|
28
|
-
const input = container.querySelector('[data-slot="input"]') as HTMLInputElement;
|
|
29
|
-
expect(input).toBeDisabled();
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
it("should call onChange handler when value changes", async () => {
|
|
33
|
-
const user = userEvent.setup();
|
|
34
|
-
const handleChange = vi.fn();
|
|
35
|
-
const { container } = render(<Input onChange={handleChange} />);
|
|
36
|
-
const input = container.querySelector('[data-slot="input"]') as HTMLInputElement;
|
|
37
|
-
await user.type(input, "test");
|
|
38
|
-
expect(handleChange).toHaveBeenCalled();
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
it("should apply aria-invalid attribute when invalid", () => {
|
|
42
|
-
const { container } = render(<Input aria-invalid="true" />);
|
|
43
|
-
const input = container.querySelector('[data-slot="input"]');
|
|
44
|
-
expect(input).toHaveAttribute("aria-invalid", "true");
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
it("should support different input types", () => {
|
|
48
|
-
const { container } = render(<Input type="email" />);
|
|
49
|
-
const input = container.querySelector('[data-slot="input"]') as HTMLInputElement;
|
|
50
|
-
expect(input.type).toBe("email");
|
|
51
|
-
});
|
|
52
|
-
});
|
|
53
|
-
|
package/src/components/input.tsx
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import * as React from "react"
|
|
2
|
-
|
|
3
|
-
import { cn } from "@/lib/utils"
|
|
4
|
-
|
|
5
|
-
function Input({ className, type, ...props }: React.ComponentProps<"input">) {
|
|
6
|
-
return (
|
|
7
|
-
<input
|
|
8
|
-
type={type}
|
|
9
|
-
data-slot="input"
|
|
10
|
-
className={cn(
|
|
11
|
-
"file:text-foreground placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground dark:bg-input/30 border-input h-9 w-full min-w-0 rounded-md border bg-transparent px-3 py-1 text-base shadow-xs transition-[color,box-shadow] outline-none file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
|
|
12
|
-
"focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]",
|
|
13
|
-
"aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
|
|
14
|
-
className
|
|
15
|
-
)}
|
|
16
|
-
{...props}
|
|
17
|
-
/>
|
|
18
|
-
)
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export { Input }
|