@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.
Files changed (219) hide show
  1. package/dist/components/accordion.d.ts +7 -0
  2. package/dist/components/alert-dialog.d.ts +14 -0
  3. package/dist/components/alert.d.ts +9 -0
  4. package/dist/components/aspect-ratio.d.ts +3 -0
  5. package/dist/components/avatar.d.ts +6 -0
  6. package/dist/components/badge.d.ts +9 -0
  7. package/dist/components/breadcrumb.d.ts +11 -0
  8. package/dist/components/button-group.d.ts +11 -0
  9. package/dist/components/button.d.ts +10 -0
  10. package/dist/components/calendar.d.ts +8 -0
  11. package/dist/components/card.d.ts +9 -0
  12. package/dist/components/carousel.d.ts +19 -0
  13. package/dist/components/chart.d.ts +40 -0
  14. package/dist/components/checkbox.d.ts +4 -0
  15. package/dist/components/collapsible.d.ts +5 -0
  16. package/dist/components/command.d.ts +18 -0
  17. package/dist/components/context-menu.d.ts +25 -0
  18. package/dist/components/dialog.d.ts +15 -0
  19. package/dist/components/drawer.d.ts +13 -0
  20. package/dist/components/dropdown-menu.d.ts +25 -0
  21. package/dist/components/empty.d.ts +11 -0
  22. package/dist/components/field.d.ts +24 -0
  23. package/dist/components/form.d.ts +24 -0
  24. package/dist/components/hover-card.d.ts +6 -0
  25. package/dist/components/input-group.d.ts +16 -0
  26. package/dist/components/input-otp.d.ts +11 -0
  27. package/dist/components/input.d.ts +3 -0
  28. package/dist/components/item.d.ts +23 -0
  29. package/dist/components/kbd.d.ts +3 -0
  30. package/dist/components/label.d.ts +4 -0
  31. package/dist/components/menubar.d.ts +26 -0
  32. package/dist/components/navigation-menu.d.ts +14 -0
  33. package/dist/components/pagination.d.ts +13 -0
  34. package/dist/components/popover.d.ts +7 -0
  35. package/dist/components/progress.d.ts +4 -0
  36. package/dist/components/radio-group.d.ts +5 -0
  37. package/dist/components/resizable.d.ts +8 -0
  38. package/dist/components/scroll-area.d.ts +5 -0
  39. package/dist/components/select.d.ts +15 -0
  40. package/dist/components/separator.d.ts +4 -0
  41. package/dist/components/sheet.d.ts +13 -0
  42. package/dist/components/sidebar.d.ts +69 -0
  43. package/dist/components/skeleton.d.ts +2 -0
  44. package/dist/components/slider.d.ts +4 -0
  45. package/dist/components/sonner.d.ts +3 -0
  46. package/dist/components/spinner.d.ts +2 -0
  47. package/dist/components/switch.d.ts +4 -0
  48. package/dist/components/table.d.ts +10 -0
  49. package/dist/components/tabs.d.ts +7 -0
  50. package/dist/components/textarea.d.ts +3 -0
  51. package/dist/components/toggle-group.d.ts +9 -0
  52. package/dist/components/toggle.d.ts +9 -0
  53. package/dist/components/tooltip.d.ts +7 -0
  54. package/dist/design-system.css +1 -0
  55. package/dist/design-system.es.js +30200 -0
  56. package/dist/design-system.umd.js +260 -0
  57. package/dist/foundations/ThemeProvider.d.ts +13 -0
  58. package/dist/hooks/use-mobile.d.ts +1 -0
  59. package/dist/index.d.ts +54 -0
  60. package/dist/lib/utils.d.ts +2 -0
  61. package/package.json +14 -1
  62. package/.husky/pre-push +0 -21
  63. package/.storybook/main.ts +0 -11
  64. package/.storybook/preview.tsx +0 -30
  65. package/.vscode/settings.json +0 -12
  66. package/.vscode/tailwind.json +0 -105
  67. package/bitbucket-pipelines.yml +0 -50
  68. package/components.json +0 -21
  69. package/eslint.config.js +0 -38
  70. package/src/components/accordion.stories.tsx +0 -258
  71. package/src/components/accordion.test.tsx +0 -390
  72. package/src/components/accordion.tsx +0 -64
  73. package/src/components/alert-dialog.stories.tsx +0 -213
  74. package/src/components/alert-dialog.test.tsx +0 -80
  75. package/src/components/alert-dialog.tsx +0 -155
  76. package/src/components/alert.stories.tsx +0 -84
  77. package/src/components/alert.test.tsx +0 -35
  78. package/src/components/alert.tsx +0 -66
  79. package/src/components/aspect-ratio.stories.tsx +0 -97
  80. package/src/components/aspect-ratio.test.tsx +0 -47
  81. package/src/components/aspect-ratio.tsx +0 -11
  82. package/src/components/avatar.stories.tsx +0 -76
  83. package/src/components/avatar.test.tsx +0 -50
  84. package/src/components/avatar.tsx +0 -51
  85. package/src/components/badge.stories.tsx +0 -64
  86. package/src/components/badge.test.tsx +0 -34
  87. package/src/components/badge.tsx +0 -46
  88. package/src/components/breadcrumb.stories.tsx +0 -86
  89. package/src/components/breadcrumb.test.tsx +0 -74
  90. package/src/components/breadcrumb.tsx +0 -109
  91. package/src/components/button-group.stories.tsx +0 -62
  92. package/src/components/button-group.tsx +0 -83
  93. package/src/components/button.stories.tsx +0 -118
  94. package/src/components/button.test.tsx +0 -64
  95. package/src/components/button.tsx +0 -62
  96. package/src/components/calendar.stories.tsx +0 -81
  97. package/src/components/calendar.tsx +0 -220
  98. package/src/components/card.stories.tsx +0 -110
  99. package/src/components/card.test.tsx +0 -56
  100. package/src/components/card.tsx +0 -92
  101. package/src/components/carousel.stories.tsx +0 -90
  102. package/src/components/carousel.tsx +0 -239
  103. package/src/components/chart.tsx +0 -357
  104. package/src/components/checkbox.stories.tsx +0 -108
  105. package/src/components/checkbox.test.tsx +0 -67
  106. package/src/components/checkbox.tsx +0 -32
  107. package/src/components/collapsible.stories.tsx +0 -106
  108. package/src/components/collapsible.test.tsx +0 -92
  109. package/src/components/collapsible.tsx +0 -31
  110. package/src/components/command.stories.tsx +0 -90
  111. package/src/components/command.tsx +0 -182
  112. package/src/components/context-menu.stories.tsx +0 -63
  113. package/src/components/context-menu.tsx +0 -252
  114. package/src/components/dialog.stories.tsx +0 -128
  115. package/src/components/dialog.tsx +0 -141
  116. package/src/components/drawer.stories.tsx +0 -104
  117. package/src/components/drawer.tsx +0 -135
  118. package/src/components/dropdown-menu.stories.tsx +0 -97
  119. package/src/components/dropdown-menu.tsx +0 -255
  120. package/src/components/empty.stories.tsx +0 -90
  121. package/src/components/empty.test.tsx +0 -55
  122. package/src/components/empty.tsx +0 -104
  123. package/src/components/field.tsx +0 -246
  124. package/src/components/form.tsx +0 -168
  125. package/src/components/hover-card.stories.tsx +0 -66
  126. package/src/components/hover-card.tsx +0 -44
  127. package/src/components/input-group.stories.tsx +0 -57
  128. package/src/components/input-group.test.tsx +0 -40
  129. package/src/components/input-group.tsx +0 -170
  130. package/src/components/input-otp.stories.tsx +0 -94
  131. package/src/components/input-otp.test.tsx +0 -60
  132. package/src/components/input-otp.tsx +0 -75
  133. package/src/components/input.stories.tsx +0 -94
  134. package/src/components/input.test.tsx +0 -53
  135. package/src/components/input.tsx +0 -21
  136. package/src/components/item.tsx +0 -193
  137. package/src/components/kbd.stories.tsx +0 -100
  138. package/src/components/kbd.test.tsx +0 -28
  139. package/src/components/kbd.tsx +0 -28
  140. package/src/components/label.stories.tsx +0 -48
  141. package/src/components/label.test.tsx +0 -28
  142. package/src/components/label.tsx +0 -24
  143. package/src/components/menubar.tsx +0 -274
  144. package/src/components/navigation-menu.tsx +0 -168
  145. package/src/components/pagination.stories.tsx +0 -107
  146. package/src/components/pagination.tsx +0 -127
  147. package/src/components/popover.stories.tsx +0 -102
  148. package/src/components/popover.tsx +0 -48
  149. package/src/components/progress.stories.tsx +0 -76
  150. package/src/components/progress.test.tsx +0 -36
  151. package/src/components/progress.tsx +0 -29
  152. package/src/components/radio-group.stories.tsx +0 -73
  153. package/src/components/radio-group.test.tsx +0 -74
  154. package/src/components/radio-group.tsx +0 -45
  155. package/src/components/resizable.stories.tsx +0 -120
  156. package/src/components/resizable.tsx +0 -54
  157. package/src/components/scroll-area.stories.tsx +0 -64
  158. package/src/components/scroll-area.test.tsx +0 -46
  159. package/src/components/scroll-area.tsx +0 -58
  160. package/src/components/select.stories.tsx +0 -111
  161. package/src/components/select.test.tsx +0 -90
  162. package/src/components/select.tsx +0 -188
  163. package/src/components/separator.stories.tsx +0 -76
  164. package/src/components/separator.test.tsx +0 -24
  165. package/src/components/separator.tsx +0 -28
  166. package/src/components/sheet.stories.tsx +0 -122
  167. package/src/components/sheet.tsx +0 -137
  168. package/src/components/sidebar.tsx +0 -726
  169. package/src/components/skeleton.stories.tsx +0 -53
  170. package/src/components/skeleton.test.tsx +0 -24
  171. package/src/components/skeleton.tsx +0 -13
  172. package/src/components/slider.stories.tsx +0 -97
  173. package/src/components/slider.test.tsx +0 -49
  174. package/src/components/slider.tsx +0 -63
  175. package/src/components/sonner.stories.tsx +0 -96
  176. package/src/components/sonner.tsx +0 -38
  177. package/src/components/spinner.stories.tsx +0 -54
  178. package/src/components/spinner.test.tsx +0 -30
  179. package/src/components/spinner.tsx +0 -16
  180. package/src/components/switch.stories.tsx +0 -108
  181. package/src/components/switch.test.tsx +0 -62
  182. package/src/components/switch.tsx +0 -31
  183. package/src/components/table.stories.tsx +0 -139
  184. package/src/components/table.test.tsx +0 -85
  185. package/src/components/table.tsx +0 -114
  186. package/src/components/tabs.stories.tsx +0 -99
  187. package/src/components/tabs.test.tsx +0 -64
  188. package/src/components/tabs.tsx +0 -66
  189. package/src/components/textarea.stories.tsx +0 -89
  190. package/src/components/textarea.test.tsx +0 -53
  191. package/src/components/textarea.tsx +0 -18
  192. package/src/components/toggle-group.stories.tsx +0 -108
  193. package/src/components/toggle-group.test.tsx +0 -66
  194. package/src/components/toggle-group.tsx +0 -81
  195. package/src/components/toggle.stories.tsx +0 -98
  196. package/src/components/toggle.test.tsx +0 -42
  197. package/src/components/toggle.tsx +0 -45
  198. package/src/components/tooltip.stories.tsx +0 -111
  199. package/src/components/tooltip.tsx +0 -61
  200. package/src/foundations/README.md +0 -141
  201. package/src/foundations/ThemeProvider.tsx +0 -77
  202. package/src/foundations/color.css +0 -232
  203. package/src/foundations/color.stories.tsx +0 -719
  204. package/src/foundations/palette.css +0 -249
  205. package/src/foundations/spacing.css +0 -8
  206. package/src/foundations/typography.css +0 -143
  207. package/src/foundations/typography.stories.tsx +0 -17
  208. package/src/hooks/use-mobile.ts +0 -19
  209. package/src/index.css +0 -176
  210. package/src/index.ts +0 -336
  211. package/src/lib/utils.ts +0 -6
  212. package/src/test/setup.ts +0 -8
  213. package/src/vite-env.d.ts +0 -1
  214. package/tsconfig.app.json +0 -33
  215. package/tsconfig.json +0 -13
  216. package/tsconfig.node.json +0 -25
  217. package/vite.config.ts +0 -30
  218. package/vitest.config.ts +0 -25
  219. /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
-
@@ -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 }