gladvn 0.2.36 → 0.2.37
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/README.md +1 -1
- package/package.json +1 -1
- package/scripts/strip-comments.ts +79 -0
- package/src/blocks/auth-form.tsx +34 -30
- package/src/blocks/auth-recovery.tsx +9 -13
- package/src/blocks/auth-split.tsx +46 -36
- package/src/blocks/settings.tsx +84 -117
- package/src/components/macro/alert-preset.tsx +3 -1
- package/src/components/macro/carousel-preset.tsx +0 -1
- package/src/components/macro/checkbox-preset.test.tsx +2 -2
- package/src/components/macro/checkbox-preset.tsx +1 -2
- package/src/components/macro/command-preset.tsx +1 -1
- package/src/components/macro/date-picker.tsx +5 -18
- package/src/components/macro/dialog-preset.tsx +5 -3
- package/src/components/macro/input-preset.tsx +0 -1
- package/src/components/macro/select-preset.tsx +1 -1
- package/src/components/macro/switch-preset.test.tsx +1 -1
- package/src/components/micro/accordion.test.tsx +6 -12
- package/src/components/micro/alert-dialog.tsx +0 -2
- package/src/components/micro/alert.test.tsx +5 -8
- package/src/components/micro/alert.tsx +0 -2
- package/src/components/micro/badge.test.tsx +5 -7
- package/src/components/micro/badge.tsx +1 -3
- package/src/components/micro/button.test.tsx +19 -26
- package/src/components/micro/button.tsx +3 -11
- package/src/components/micro/calendar.tsx +12 -13
- package/src/components/micro/card.test.tsx +3 -5
- package/src/components/micro/carousel.tsx +0 -1
- package/src/components/micro/chart.tsx +3 -2
- package/src/components/micro/checkbox.test.tsx +9 -18
- package/src/components/micro/combobox.tsx +1 -3
- package/src/components/micro/command.tsx +23 -11
- package/src/components/micro/context-menu.tsx +0 -2
- package/src/components/micro/dialog.test.tsx +6 -14
- package/src/components/micro/dialog.tsx +0 -2
- package/src/components/micro/drawer.tsx +0 -3
- package/src/components/micro/dropdown-menu.test.tsx +8 -14
- package/src/components/micro/dropdown-menu.tsx +0 -2
- package/src/components/micro/field.tsx +3 -3
- package/src/components/micro/hover-card.tsx +0 -2
- package/src/components/micro/input-group.tsx +9 -42
- package/src/components/micro/input.test.tsx +15 -24
- package/src/components/micro/navigation-menu.tsx +0 -2
- package/src/components/micro/popover.test.tsx +5 -9
- package/src/components/micro/progress.tsx +5 -1
- package/src/components/micro/select.test.tsx +5 -18
- package/src/components/micro/select.tsx +3 -5
- package/src/components/micro/separator.tsx +1 -1
- package/src/components/micro/sheet.test.tsx +6 -14
- package/src/components/micro/sheet.tsx +0 -2
- package/src/components/micro/sidebar.tsx +5 -6
- package/src/components/micro/sonner.tsx +0 -1
- package/src/components/micro/switch.test.tsx +9 -18
- package/src/components/micro/switch.tsx +1 -1
- package/src/components/micro/tabs.test.tsx +5 -11
- package/src/components/micro/theme-provider.tsx +0 -12
- package/src/components/micro/toggle-group.tsx +1 -12
- package/src/components/micro/tooltip.test.tsx +7 -12
- package/src/dev/App.tsx +6 -6
- package/src/dev/components/BlockViewer.tsx +15 -4
- package/src/dev/components/Container.tsx +20 -0
- package/src/dev/components/GladvnLogo.tsx +3 -9
- package/src/dev/components/code-highlighter.tsx +5 -3
- package/src/dev/components/showcase.tsx +116 -168
- package/src/dev/sections/buttons.tsx +4 -9
- package/src/dev/sections/display.tsx +68 -72
- package/src/dev/sections/feedback.tsx +304 -314
- package/src/dev/sections/forms.tsx +10 -14
- package/src/dev/sections/interactive.tsx +32 -32
- package/src/dev/sections/overview.tsx +2 -10
- package/src/dev/showcase/accordion.tsx +512 -554
- package/src/dev/showcase/alert-dialog.tsx +525 -565
- package/src/dev/showcase/alert.tsx +352 -402
- package/src/dev/showcase/aspect-ratio.tsx +167 -193
- package/src/dev/showcase/avatar.tsx +231 -269
- package/src/dev/showcase/badge.tsx +92 -140
- package/src/dev/showcase/breadcrumb.tsx +113 -135
- package/src/dev/showcase/button.tsx +140 -173
- package/src/dev/showcase/calendar.tsx +187 -237
- package/src/dev/showcase/card.tsx +229 -261
- package/src/dev/showcase/carousel.tsx +288 -315
- package/src/dev/showcase/chart.tsx +44 -50
- package/src/dev/showcase/checkbox.tsx +286 -330
- package/src/dev/showcase/collapsible.tsx +143 -160
- package/src/dev/showcase/combobox.tsx +327 -369
- package/src/dev/showcase/command.tsx +106 -120
- package/src/dev/showcase/context-menu.tsx +93 -107
- package/src/dev/showcase/dialog.tsx +585 -630
- package/src/dev/showcase/drawer.tsx +176 -196
- package/src/dev/showcase/dropdown-menu.tsx +183 -201
- package/src/dev/showcase/empty.tsx +232 -274
- package/src/dev/showcase/hover-card.tsx +153 -180
- package/src/dev/showcase/input-group.tsx +221 -253
- package/src/dev/showcase/input-otp.tsx +204 -250
- package/src/dev/showcase/input.tsx +246 -325
- package/src/dev/showcase/item.tsx +134 -152
- package/src/dev/showcase/kbd.tsx +149 -172
- package/src/dev/showcase/label.tsx +50 -68
- package/src/dev/showcase/menubar.tsx +194 -213
- package/src/dev/showcase/navigation-menu.tsx +119 -133
- package/src/dev/showcase/overview-components/ArchitectureSection.tsx +126 -0
- package/src/dev/showcase/overview-components/CodeBlock.tsx +50 -0
- package/src/dev/showcase/overview-components/ColorPaletteSection.tsx +44 -0
- package/src/dev/showcase/overview-components/DashboardPreview.tsx +159 -0
- package/src/dev/showcase/overview-components/FeatureGridSection.tsx +535 -0
- package/src/dev/showcase/overview-components/FooterCTA.tsx +137 -0
- package/src/dev/showcase/overview-components/HeroSection.tsx +65 -0
- package/src/dev/showcase/overview-components/PrinciplesSection.tsx +128 -0
- package/src/dev/showcase/overview.tsx +21 -1163
- package/src/dev/showcase/pagination.tsx +144 -190
- package/src/dev/showcase/popover.tsx +181 -196
- package/src/dev/showcase/progress.tsx +141 -189
- package/src/dev/showcase/radio-group.tsx +272 -332
- package/src/dev/showcase/resizable.tsx +119 -141
- package/src/dev/showcase/scroll-area.tsx +21 -36
- package/src/dev/showcase/select.tsx +262 -322
- package/src/dev/showcase/separator.tsx +75 -73
- package/src/dev/showcase/sheet.tsx +333 -361
- package/src/dev/showcase/sidebar.tsx +53 -59
- package/src/dev/showcase/skeleton.tsx +93 -112
- package/src/dev/showcase/slider.tsx +244 -296
- package/src/dev/showcase/sonner.tsx +251 -258
- package/src/dev/showcase/spinner.tsx +46 -67
- package/src/dev/showcase/switch.tsx +298 -362
- package/src/dev/showcase/table.tsx +76 -80
- package/src/dev/showcase/tabs.tsx +302 -355
- package/src/dev/showcase/textarea.tsx +240 -293
- package/src/dev/showcase/toast.tsx +48 -54
- package/src/dev/showcase/toggle-group.tsx +104 -129
- package/src/dev/showcase/toggle.tsx +82 -102
- package/src/dev/showcase/tooltip.tsx +173 -192
- package/src/dev/showcase/why.tsx +6 -12
- package/src/hooks/use-mobile.test.ts +3 -2
- package/src/lib/utils.test.ts +5 -4
|
@@ -35,7 +35,7 @@ const badgeVariants = cva(
|
|
|
35
35
|
},
|
|
36
36
|
},
|
|
37
37
|
compoundVariants: [
|
|
38
|
-
|
|
38
|
+
|
|
39
39
|
{
|
|
40
40
|
variant: "solid",
|
|
41
41
|
color: "primary",
|
|
@@ -82,7 +82,6 @@ const badgeVariants = cva(
|
|
|
82
82
|
className: "bg-accent text-accent-foreground",
|
|
83
83
|
},
|
|
84
84
|
|
|
85
|
-
// Outline
|
|
86
85
|
{
|
|
87
86
|
variant: "outline",
|
|
88
87
|
color: "primary",
|
|
@@ -125,7 +124,6 @@ const badgeVariants = cva(
|
|
|
125
124
|
className: "border-accent text-accent-foreground",
|
|
126
125
|
},
|
|
127
126
|
|
|
128
|
-
// Soft
|
|
129
127
|
{
|
|
130
128
|
variant: "soft",
|
|
131
129
|
color: "primary",
|
|
@@ -7,82 +7,77 @@ import { describe, expect, it, vi } from "vitest";
|
|
|
7
7
|
import { Button } from "./button";
|
|
8
8
|
|
|
9
9
|
describe("Button", () => {
|
|
10
|
-
|
|
10
|
+
|
|
11
11
|
it("[3C.1-01] [P1] renders correctly with default props", () => {
|
|
12
|
-
|
|
12
|
+
|
|
13
13
|
render(<Button>Click me</Button>);
|
|
14
14
|
const button = screen.getByRole("button", { name: "Click me" });
|
|
15
|
-
|
|
15
|
+
|
|
16
16
|
expect(button).toBeInTheDocument();
|
|
17
17
|
expect(button).toHaveAttribute("data-variant", "solid");
|
|
18
18
|
expect(button).toHaveAttribute("data-color", "primary");
|
|
19
19
|
});
|
|
20
20
|
|
|
21
21
|
it("[3C.1-02] [P1] applies custom variants and colors", () => {
|
|
22
|
-
|
|
22
|
+
|
|
23
23
|
render(
|
|
24
24
|
<Button variant="outline" color="destructive">
|
|
25
25
|
Delete
|
|
26
26
|
</Button>,
|
|
27
27
|
);
|
|
28
28
|
const button = screen.getByRole("button", { name: "Delete" });
|
|
29
|
-
|
|
29
|
+
|
|
30
30
|
expect(button).toHaveAttribute("data-variant", "outline");
|
|
31
31
|
expect(button).toHaveAttribute("data-color", "destructive");
|
|
32
32
|
});
|
|
33
33
|
|
|
34
34
|
it("[3C.1-03] [P1] merges custom classNames properly", () => {
|
|
35
|
-
|
|
35
|
+
|
|
36
36
|
render(<Button className="custom-test-class">Classy Button</Button>);
|
|
37
37
|
const button = screen.getByRole("button", { name: "Classy Button" });
|
|
38
|
-
|
|
38
|
+
|
|
39
39
|
expect(button).toHaveClass("custom-test-class");
|
|
40
40
|
});
|
|
41
41
|
|
|
42
42
|
it("[3C.1-04] [P1] supports iconOnly attribute to render as square", () => {
|
|
43
|
-
|
|
43
|
+
|
|
44
44
|
render(<Button iconOnly>Icon</Button>);
|
|
45
45
|
const button = screen.getByRole("button", { name: "Icon" });
|
|
46
|
-
|
|
46
|
+
|
|
47
47
|
expect(button).toHaveAttribute("data-icon", "true");
|
|
48
48
|
});
|
|
49
49
|
|
|
50
|
-
// 2. Behaviors (Interaction & Events)
|
|
51
50
|
it("[3C.1-05] [P1] fires onClick event when clicked", async () => {
|
|
52
|
-
|
|
51
|
+
|
|
53
52
|
const user = userEvent.setup();
|
|
54
53
|
const handleClick = vi.fn();
|
|
55
54
|
render(<Button onClick={handleClick}>Click</Button>);
|
|
56
55
|
|
|
57
56
|
const button = screen.getByRole("button", { name: "Click" });
|
|
58
|
-
|
|
57
|
+
|
|
59
58
|
await user.click(button);
|
|
60
59
|
|
|
61
|
-
// Then
|
|
62
60
|
expect(handleClick).toHaveBeenCalledTimes(1);
|
|
63
61
|
});
|
|
64
62
|
|
|
65
63
|
it("[3C.1-06] [P1] can be focused and activated via keyboard", async () => {
|
|
66
|
-
|
|
64
|
+
|
|
67
65
|
const user = userEvent.setup();
|
|
68
66
|
const handleClick = vi.fn();
|
|
69
67
|
render(<Button onClick={handleClick}>Submit</Button>);
|
|
70
68
|
|
|
71
69
|
const button = screen.getByRole("button", { name: "Submit" });
|
|
72
70
|
|
|
73
|
-
// Test focus
|
|
74
|
-
// When
|
|
75
71
|
await user.tab();
|
|
76
|
-
|
|
72
|
+
|
|
77
73
|
expect(button).toHaveFocus();
|
|
78
74
|
|
|
79
|
-
// Test keyboard activation (Enter)
|
|
80
75
|
await user.keyboard("{Enter}");
|
|
81
76
|
expect(handleClick).toHaveBeenCalledTimes(1);
|
|
82
77
|
});
|
|
83
78
|
|
|
84
79
|
it("[3C.1-07] [P1] handles disabled state correctly and blocks clicks", async () => {
|
|
85
|
-
|
|
80
|
+
|
|
86
81
|
const user = userEvent.setup();
|
|
87
82
|
const handleClick = vi.fn();
|
|
88
83
|
render(
|
|
@@ -92,29 +87,27 @@ describe("Button", () => {
|
|
|
92
87
|
);
|
|
93
88
|
|
|
94
89
|
const button = screen.getByRole("button", { name: "Disabled Button" });
|
|
95
|
-
|
|
90
|
+
|
|
96
91
|
expect(button).toBeDisabled();
|
|
97
92
|
|
|
98
|
-
// When
|
|
99
93
|
await user.click(button);
|
|
100
94
|
expect(handleClick).not.toHaveBeenCalled();
|
|
101
95
|
});
|
|
102
96
|
|
|
103
|
-
// 3. Architecture & Integration
|
|
104
97
|
it("[3C.1-08] [P1] forwards the ref to the underlying DOM element", () => {
|
|
105
|
-
|
|
98
|
+
|
|
106
99
|
const ref = React.createRef<HTMLButtonElement>();
|
|
107
100
|
render(<Button ref={ref}>Ref Button</Button>);
|
|
108
|
-
|
|
101
|
+
|
|
109
102
|
expect(ref.current).toBeInstanceOf(HTMLButtonElement);
|
|
110
103
|
expect(ref.current?.textContent).toBe("Ref Button");
|
|
111
104
|
});
|
|
112
105
|
|
|
113
106
|
it("[3C.1-09] [P1] supports polymorphism via render prop (Base UI 'render' pattern)", () => {
|
|
114
|
-
|
|
107
|
+
|
|
115
108
|
render(<Button render={<a href="/home" />}>Link Button</Button>);
|
|
116
109
|
const link = screen.getByText("Link Button");
|
|
117
|
-
|
|
110
|
+
|
|
118
111
|
expect(link).toBeInTheDocument();
|
|
119
112
|
expect(link.tagName).toBe("A");
|
|
120
113
|
expect(link).toHaveAttribute("href", "/home");
|
|
@@ -36,7 +36,7 @@ const buttonVariants = cva(
|
|
|
36
36
|
"group/button inline-flex shrink-0 items-center justify-center rounded-lg border border-transparent bg-clip-border text-sm font-medium whitespace-nowrap transition-all outline-none select-none focus-visible:ring-3 focus-visible:ring-offset-1 focus-visible:ring-offset-background active:not-aria-[haspopup]:translate-y-px disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:focus-visible:ring-3 aria-invalid:focus-visible:ring-destructive/50 dark:aria-invalid:border-destructive/50 dark:aria-invalid:focus-visible:ring-destructive/50 data-[icon=true]:aspect-square data-[icon=true]:px-0",
|
|
37
37
|
{
|
|
38
38
|
variants: {
|
|
39
|
-
|
|
39
|
+
|
|
40
40
|
variant: {
|
|
41
41
|
solid: "border-transparent",
|
|
42
42
|
outline: "bg-transparent",
|
|
@@ -45,8 +45,6 @@ const buttonVariants = cva(
|
|
|
45
45
|
link: "border-transparent bg-transparent underline-offset-4 hover:underline",
|
|
46
46
|
},
|
|
47
47
|
|
|
48
|
-
// ── Semantic color ───────────────────────────────────────────
|
|
49
|
-
// Left empty — actual classes are injected via compoundVariants below
|
|
50
48
|
color: {
|
|
51
49
|
primary: "",
|
|
52
50
|
secondary: "",
|
|
@@ -59,7 +57,6 @@ const buttonVariants = cva(
|
|
|
59
57
|
accent: "",
|
|
60
58
|
},
|
|
61
59
|
|
|
62
|
-
// ── Size ─────────────────────────────────────────────────────
|
|
63
60
|
size: {
|
|
64
61
|
sm: "h-7 gap-1 px-3 text-xs",
|
|
65
62
|
md: "h-8 gap-1.5 px-3.5",
|
|
@@ -67,9 +64,8 @@ const buttonVariants = cva(
|
|
|
67
64
|
},
|
|
68
65
|
},
|
|
69
66
|
|
|
70
|
-
// ── variant × color combinations ─────────────────────────────
|
|
71
67
|
compoundVariants: [
|
|
72
|
-
|
|
68
|
+
|
|
73
69
|
{
|
|
74
70
|
variant: "solid",
|
|
75
71
|
color: "primary",
|
|
@@ -125,7 +121,6 @@ const buttonVariants = cva(
|
|
|
125
121
|
"bg-accent text-accent-foreground hover:bg-accent/85",
|
|
126
122
|
},
|
|
127
123
|
|
|
128
|
-
// outline — border + semantic text, tinted hover
|
|
129
124
|
{
|
|
130
125
|
variant: "outline",
|
|
131
126
|
color: "primary",
|
|
@@ -174,7 +169,6 @@ const buttonVariants = cva(
|
|
|
174
169
|
"border-accent text-accent-foreground hover:bg-accent/10",
|
|
175
170
|
},
|
|
176
171
|
|
|
177
|
-
// soft — tinted background, semantic text
|
|
178
172
|
{
|
|
179
173
|
variant: "soft",
|
|
180
174
|
color: "primary",
|
|
@@ -222,7 +216,6 @@ const buttonVariants = cva(
|
|
|
222
216
|
"bg-accent/50 text-accent-foreground hover:bg-accent/80",
|
|
223
217
|
},
|
|
224
218
|
|
|
225
|
-
// ghost — transparent, semantic text, tinted hover
|
|
226
219
|
{
|
|
227
220
|
variant: "ghost",
|
|
228
221
|
color: "primary",
|
|
@@ -269,7 +262,6 @@ const buttonVariants = cva(
|
|
|
269
262
|
className: "text-accent-foreground hover:bg-accent/10",
|
|
270
263
|
},
|
|
271
264
|
|
|
272
|
-
// link — just semantic text + underline on hover
|
|
273
265
|
{ variant: "link", color: "primary", className: "text-primary" },
|
|
274
266
|
{ variant: "link", color: "secondary", className: "text-secondary" },
|
|
275
267
|
{ variant: "link", color: "destructive", className: "text-destructive" },
|
|
@@ -279,7 +271,7 @@ const buttonVariants = cva(
|
|
|
279
271
|
{ variant: "link", color: "tertiary", className: "text-tertiary" },
|
|
280
272
|
{ variant: "link", color: "muted", className: "text-muted-foreground" },
|
|
281
273
|
{ variant: "link", color: "accent", className: "text-accent-foreground" },
|
|
282
|
-
|
|
274
|
+
|
|
283
275
|
{ color: "primary", className: "focus-visible:ring-primary/50" },
|
|
284
276
|
{ color: "secondary", className: "focus-visible:ring-secondary/50" },
|
|
285
277
|
{ color: "destructive", className: "focus-visible:ring-destructive/50" },
|
|
@@ -345,31 +345,30 @@ function CalendarDayButton({
|
|
|
345
345
|
data-range-end={modifiers.range_end}
|
|
346
346
|
data-range-middle={modifiers.range_middle}
|
|
347
347
|
className={cn(
|
|
348
|
-
|
|
348
|
+
|
|
349
349
|
"relative isolate z-10 flex aspect-square size-7 flex-col items-center justify-center gap-1 rounded-md font-normal leading-none transition-colors outline-none select-none",
|
|
350
350
|
"group-[.calendar-sm]/calendar:size-6 group-[.calendar-lg]/calendar:size-8",
|
|
351
|
-
|
|
351
|
+
|
|
352
352
|
"text-sm group-[.calendar-sm]/calendar:text-xs",
|
|
353
|
-
|
|
353
|
+
|
|
354
354
|
"hover:bg-accent hover:text-accent-foreground",
|
|
355
|
-
|
|
355
|
+
|
|
356
356
|
"focus-visible:ring-3 focus-visible:ring-ring/50 focus-visible:ring-offset-1 focus-visible:ring-offset-background",
|
|
357
|
-
|
|
357
|
+
|
|
358
358
|
"active:translate-y-px",
|
|
359
|
-
|
|
359
|
+
|
|
360
360
|
"disabled:pointer-events-none disabled:opacity-50",
|
|
361
|
-
|
|
361
|
+
|
|
362
362
|
"group-data-[focused=true]/day:relative group-data-[focused=true]/day:z-10 group-data-[focused=true]/day:ring-3 group-data-[focused=true]/day:ring-ring/50",
|
|
363
|
-
|
|
363
|
+
|
|
364
364
|
"data-[selected-single=true]:bg-primary data-[selected-single=true]:text-primary-foreground data-[selected-single=true]:hover:bg-primary/90",
|
|
365
|
-
|
|
365
|
+
|
|
366
366
|
"data-[range-start=true]:bg-primary data-[range-start=true]:text-primary-foreground",
|
|
367
|
-
|
|
367
|
+
|
|
368
368
|
"data-[range-end=true]:bg-primary data-[range-end=true]:text-primary-foreground",
|
|
369
|
-
|
|
369
|
+
|
|
370
370
|
"data-[range-middle=true]:bg-muted data-[range-middle=true]:text-foreground",
|
|
371
|
-
|
|
372
|
-
// Dark mode
|
|
371
|
+
|
|
373
372
|
"dark:hover:text-foreground",
|
|
374
373
|
className,
|
|
375
374
|
)}
|
|
@@ -13,7 +13,7 @@ import {
|
|
|
13
13
|
|
|
14
14
|
describe("Card", () => {
|
|
15
15
|
it("[3C.11-01] [P1] renders all card parts correctly", () => {
|
|
16
|
-
|
|
16
|
+
|
|
17
17
|
render(
|
|
18
18
|
<Card data-testid="card">
|
|
19
19
|
<CardHeader>
|
|
@@ -25,7 +25,6 @@ describe("Card", () => {
|
|
|
25
25
|
</Card>,
|
|
26
26
|
);
|
|
27
27
|
|
|
28
|
-
// Then
|
|
29
28
|
expect(screen.getByTestId("card")).toBeInTheDocument();
|
|
30
29
|
expect(screen.getByText("Card Title")).toBeInTheDocument();
|
|
31
30
|
expect(screen.getByText("Card Description")).toBeInTheDocument();
|
|
@@ -34,16 +33,15 @@ describe("Card", () => {
|
|
|
34
33
|
});
|
|
35
34
|
|
|
36
35
|
it("[3C.11-02] [P1] applies the size variants correctly", () => {
|
|
37
|
-
|
|
36
|
+
|
|
38
37
|
const { rerender } = render(
|
|
39
38
|
<Card size="sm" data-testid="card-sm">
|
|
40
39
|
Content
|
|
41
40
|
</Card>,
|
|
42
41
|
);
|
|
43
|
-
|
|
42
|
+
|
|
44
43
|
expect(screen.getByTestId("card-sm")).toHaveAttribute("data-size", "sm");
|
|
45
44
|
|
|
46
|
-
// When
|
|
47
45
|
rerender(
|
|
48
46
|
<Card size="lg" data-testid="card-lg">
|
|
49
47
|
Content
|
|
@@ -172,7 +172,6 @@ const CarouselContent = React.forwardRef<
|
|
|
172
172
|
>(({ className, ...props }, ref) => {
|
|
173
173
|
const { carouselRef } = useCarousel();
|
|
174
174
|
|
|
175
|
-
// Combine forwarded ref and internal embla ref
|
|
176
175
|
const setRefs = React.useCallback(
|
|
177
176
|
(node: HTMLDivElement) => {
|
|
178
177
|
carouselRef(node);
|
|
@@ -14,7 +14,6 @@ import * as RechartsPrimitive from "recharts";
|
|
|
14
14
|
|
|
15
15
|
import { cn } from "../../lib/utils";
|
|
16
16
|
|
|
17
|
-
// Format: { THEME_NAME: CSS_SELECTOR }
|
|
18
17
|
const THEMES = { light: "", dark: ".dark" } as const;
|
|
19
18
|
|
|
20
19
|
const INITIAL_DIMENSION = { width: 320, height: 200 } as const;
|
|
@@ -229,7 +228,9 @@ function ChartTooltipContent({
|
|
|
229
228
|
key={index}
|
|
230
229
|
className={cn(
|
|
231
230
|
"flex w-full flex-wrap items-stretch gap-2 [&>svg]:h-2.5 [&>svg]:w-2.5 [&>svg]:text-muted-foreground",
|
|
232
|
-
|
|
231
|
+
{
|
|
232
|
+
"items-center": indicator === "dot"
|
|
233
|
+
}
|
|
233
234
|
)}
|
|
234
235
|
>
|
|
235
236
|
{formatter && item?.value !== undefined && item.name ? (
|
|
@@ -8,16 +8,16 @@ import { Checkbox } from "./checkbox";
|
|
|
8
8
|
|
|
9
9
|
describe("Checkbox", () => {
|
|
10
10
|
it("[3C.6-01] [P1] renders correctly", () => {
|
|
11
|
-
|
|
11
|
+
|
|
12
12
|
render(<Checkbox aria-label="Accept terms" />);
|
|
13
13
|
const checkbox = screen.getByRole("checkbox", { name: "Accept terms" });
|
|
14
|
-
|
|
14
|
+
|
|
15
15
|
expect(checkbox).toBeInTheDocument();
|
|
16
16
|
expect(checkbox).toHaveAttribute("data-slot", "checkbox");
|
|
17
17
|
});
|
|
18
18
|
|
|
19
19
|
it("[3C.6-02] [P1] handles user click to toggle state", async () => {
|
|
20
|
-
|
|
20
|
+
|
|
21
21
|
const user = userEvent.setup();
|
|
22
22
|
const handleCheckedChange = vi.fn();
|
|
23
23
|
render(
|
|
@@ -26,42 +26,34 @@ describe("Checkbox", () => {
|
|
|
26
26
|
|
|
27
27
|
const checkbox = screen.getByRole("checkbox", { name: "Toggle" });
|
|
28
28
|
|
|
29
|
-
// Initially unchecked
|
|
30
|
-
// Then
|
|
31
29
|
expect(checkbox).not.toBeChecked();
|
|
32
30
|
|
|
33
|
-
// Click to check
|
|
34
|
-
// When
|
|
35
31
|
await user.click(checkbox);
|
|
36
32
|
expect(checkbox).toBeChecked();
|
|
37
33
|
expect(handleCheckedChange).toHaveBeenCalledWith(true, expect.anything());
|
|
38
34
|
|
|
39
|
-
// Click to uncheck
|
|
40
35
|
await user.click(checkbox);
|
|
41
36
|
expect(checkbox).not.toBeChecked();
|
|
42
37
|
expect(handleCheckedChange).toHaveBeenCalledWith(false, expect.anything());
|
|
43
38
|
});
|
|
44
39
|
|
|
45
40
|
it("[3C.6-03] [P1] can be focused and toggled via keyboard (Space)", async () => {
|
|
46
|
-
|
|
41
|
+
|
|
47
42
|
const user = userEvent.setup();
|
|
48
43
|
render(<Checkbox aria-label="Keyboard toggle" />);
|
|
49
44
|
|
|
50
45
|
const checkbox = screen.getByRole("checkbox", { name: "Keyboard toggle" });
|
|
51
46
|
|
|
52
|
-
// Test focus
|
|
53
|
-
// When
|
|
54
47
|
await user.tab();
|
|
55
|
-
|
|
48
|
+
|
|
56
49
|
expect(checkbox).toHaveFocus();
|
|
57
50
|
|
|
58
|
-
// Test keyboard activation (Space for checkbox)
|
|
59
51
|
await user.keyboard("[Space]");
|
|
60
52
|
expect(checkbox).toBeChecked();
|
|
61
53
|
});
|
|
62
54
|
|
|
63
55
|
it("[3C.6-04] [P1] handles disabled state correctly and blocks clicks", async () => {
|
|
64
|
-
|
|
56
|
+
|
|
65
57
|
const user = userEvent.setup();
|
|
66
58
|
const handleCheckedChange = vi.fn();
|
|
67
59
|
render(
|
|
@@ -73,20 +65,19 @@ describe("Checkbox", () => {
|
|
|
73
65
|
);
|
|
74
66
|
|
|
75
67
|
const checkbox = screen.getByRole("checkbox", { name: "Disabled" });
|
|
76
|
-
|
|
68
|
+
|
|
77
69
|
expect(checkbox).toHaveAttribute("aria-disabled", "true");
|
|
78
70
|
|
|
79
|
-
// When
|
|
80
71
|
await user.click(checkbox);
|
|
81
72
|
expect(handleCheckedChange).not.toHaveBeenCalled();
|
|
82
73
|
expect(checkbox).not.toBeChecked();
|
|
83
74
|
});
|
|
84
75
|
|
|
85
76
|
it("[3C.6-05] [P1] forwards the ref to the underlying DOM element", () => {
|
|
86
|
-
|
|
77
|
+
|
|
87
78
|
const ref = React.createRef<HTMLSpanElement>();
|
|
88
79
|
render(<Checkbox ref={ref} aria-label="Ref checkbox" />);
|
|
89
|
-
|
|
80
|
+
|
|
90
81
|
expect(ref.current).toBeInstanceOf(HTMLSpanElement);
|
|
91
82
|
});
|
|
92
83
|
});
|
|
@@ -80,7 +80,7 @@ function ComboboxInput({ className, ...props }: ComboboxPrimitive.Input.Props) {
|
|
|
80
80
|
data-slot="combobox-input"
|
|
81
81
|
className={cn(
|
|
82
82
|
"flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-sm shadow-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50",
|
|
83
|
-
|
|
83
|
+
|
|
84
84
|
"group-data-[slot=input-group]/input-group:border-0",
|
|
85
85
|
"group-data-[slot=input-group]/input-group:shadow-none",
|
|
86
86
|
"group-data-[slot=input-group]/input-group:h-auto",
|
|
@@ -239,8 +239,6 @@ function ComboboxSeparator({
|
|
|
239
239
|
);
|
|
240
240
|
}
|
|
241
241
|
|
|
242
|
-
|
|
243
|
-
|
|
244
242
|
const comboboxChipsVariants = cva(
|
|
245
243
|
"group/combobox-chips flex flex-wrap items-center gap-1 rounded-lg border border-input bg-transparent bg-clip-padding transition-colors focus-within:border-ring focus-within:ring-3 focus-within:ring-ring/50 has-aria-invalid:border-destructive has-aria-invalid:focus-within:ring-3 has-aria-invalid:focus-within:ring-destructive/20 has-[[data-slot=combobox-chip]]:px-1 dark:bg-input/30 dark:has-aria-invalid:border-destructive/50 dark:has-aria-invalid:focus-within:ring-destructive/40 has-disabled:opacity-50 has-disabled:cursor-not-allowed has-disabled:pointer-events-none",
|
|
246
244
|
{
|
|
@@ -68,17 +68,21 @@ const CommandInput = React.forwardRef<
|
|
|
68
68
|
data-slot="command-input-wrapper"
|
|
69
69
|
className={cn(
|
|
70
70
|
"pb-0",
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
71
|
+
{
|
|
72
|
+
"p-1": size === "sm",
|
|
73
|
+
"p-2": size === "md",
|
|
74
|
+
"p-3": size === "lg",
|
|
75
|
+
}
|
|
74
76
|
)}
|
|
75
77
|
>
|
|
76
78
|
<InputGroup
|
|
77
79
|
className={cn(
|
|
78
80
|
"rounded-lg border-input/30 bg-input/30 shadow-none",
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
81
|
+
{
|
|
82
|
+
"h-8": size === "sm",
|
|
83
|
+
"h-10": size === "md",
|
|
84
|
+
"h-11": size === "lg",
|
|
85
|
+
}
|
|
82
86
|
)}
|
|
83
87
|
>
|
|
84
88
|
<CommandPrimitive.Input
|
|
@@ -88,10 +92,16 @@ const CommandInput = React.forwardRef<
|
|
|
88
92
|
{...props}
|
|
89
93
|
/>
|
|
90
94
|
<InputGroupAddon
|
|
91
|
-
className={cn("shrink-0",
|
|
95
|
+
className={cn("shrink-0", {
|
|
96
|
+
"pl-3": size === "lg",
|
|
97
|
+
"pl-2": size !== "lg",
|
|
98
|
+
})}
|
|
92
99
|
>
|
|
93
100
|
<SearchIcon
|
|
94
|
-
className={cn("opacity-50",
|
|
101
|
+
className={cn("opacity-50", {
|
|
102
|
+
"size-5": size === "lg",
|
|
103
|
+
"size-4": size !== "lg",
|
|
104
|
+
})}
|
|
95
105
|
/>
|
|
96
106
|
</InputGroupAddon>
|
|
97
107
|
</InputGroup>
|
|
@@ -127,9 +137,11 @@ const CommandEmpty = React.forwardRef<
|
|
|
127
137
|
data-slot="command-empty"
|
|
128
138
|
className={cn(
|
|
129
139
|
"text-center",
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
140
|
+
{
|
|
141
|
+
"py-4 text-xs": size === "sm",
|
|
142
|
+
"py-6 text-sm": size === "md",
|
|
143
|
+
"py-8 text-base": size === "lg",
|
|
144
|
+
},
|
|
133
145
|
className,
|
|
134
146
|
)}
|
|
135
147
|
{...props}
|
|
@@ -33,8 +33,6 @@ function ContextMenu({ ...props }: ContextMenuPrimitive.Root.Props) {
|
|
|
33
33
|
}
|
|
34
34
|
ContextMenu.displayName = "ContextMenu";
|
|
35
35
|
|
|
36
|
-
|
|
37
|
-
|
|
38
36
|
const ContextMenuTrigger = React.forwardRef<
|
|
39
37
|
React.ElementRef<typeof ContextMenuPrimitive.Trigger>,
|
|
40
38
|
React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.Trigger>
|
|
@@ -16,7 +16,7 @@ import {
|
|
|
16
16
|
|
|
17
17
|
describe("Dialog", () => {
|
|
18
18
|
it("[3C.4-01] [P1] renders trigger correctly and content is hidden initially", () => {
|
|
19
|
-
|
|
19
|
+
|
|
20
20
|
render(
|
|
21
21
|
<Dialog>
|
|
22
22
|
<DialogTrigger>Open Dialog</DialogTrigger>
|
|
@@ -30,15 +30,14 @@ describe("Dialog", () => {
|
|
|
30
30
|
);
|
|
31
31
|
|
|
32
32
|
const trigger = screen.getByRole("button", { name: "Open Dialog" });
|
|
33
|
-
|
|
33
|
+
|
|
34
34
|
expect(trigger).toBeInTheDocument();
|
|
35
35
|
|
|
36
|
-
// Content should not be in the document
|
|
37
36
|
expect(screen.queryByRole("dialog")).not.toBeInTheDocument();
|
|
38
37
|
});
|
|
39
38
|
|
|
40
39
|
it("[3C.4-02] [P1] opens the dialog when trigger is clicked", async () => {
|
|
41
|
-
|
|
40
|
+
|
|
42
41
|
const user = userEvent.setup();
|
|
43
42
|
render(
|
|
44
43
|
<Dialog>
|
|
@@ -56,10 +55,9 @@ describe("Dialog", () => {
|
|
|
56
55
|
);
|
|
57
56
|
|
|
58
57
|
const trigger = screen.getByRole("button", { name: "Open Dialog" });
|
|
59
|
-
|
|
58
|
+
|
|
60
59
|
await user.click(trigger);
|
|
61
60
|
|
|
62
|
-
// Then
|
|
63
61
|
await waitFor(() => {
|
|
64
62
|
const dialog = screen.getByRole("dialog");
|
|
65
63
|
expect(dialog).toBeInTheDocument();
|
|
@@ -69,7 +67,7 @@ describe("Dialog", () => {
|
|
|
69
67
|
});
|
|
70
68
|
|
|
71
69
|
it("[3C.4-03] [P1] closes the dialog when Close button is clicked", async () => {
|
|
72
|
-
|
|
70
|
+
|
|
73
71
|
const user = userEvent.setup();
|
|
74
72
|
render(
|
|
75
73
|
<Dialog>
|
|
@@ -81,16 +79,12 @@ describe("Dialog", () => {
|
|
|
81
79
|
</Dialog>,
|
|
82
80
|
);
|
|
83
81
|
|
|
84
|
-
// Open
|
|
85
|
-
// When
|
|
86
82
|
await user.click(screen.getByRole("button", { name: "Open Dialog" }));
|
|
87
83
|
|
|
88
|
-
// Then
|
|
89
84
|
await waitFor(() => {
|
|
90
85
|
expect(screen.getByRole("dialog")).toBeInTheDocument();
|
|
91
86
|
});
|
|
92
87
|
|
|
93
|
-
// Close
|
|
94
88
|
await user.click(screen.getByRole("button", { name: "Close Me" }));
|
|
95
89
|
|
|
96
90
|
await waitFor(() => {
|
|
@@ -99,7 +93,7 @@ describe("Dialog", () => {
|
|
|
99
93
|
});
|
|
100
94
|
|
|
101
95
|
it("[3C.4-04] [P1] closes the dialog when Escape is pressed", async () => {
|
|
102
|
-
|
|
96
|
+
|
|
103
97
|
const user = userEvent.setup();
|
|
104
98
|
render(
|
|
105
99
|
<Dialog>
|
|
@@ -110,10 +104,8 @@ describe("Dialog", () => {
|
|
|
110
104
|
</Dialog>,
|
|
111
105
|
);
|
|
112
106
|
|
|
113
|
-
// When
|
|
114
107
|
await user.click(screen.getByRole("button", { name: "Open Dialog" }));
|
|
115
108
|
|
|
116
|
-
// Then
|
|
117
109
|
await waitFor(() => {
|
|
118
110
|
expect(screen.getByRole("dialog")).toBeInTheDocument();
|
|
119
111
|
});
|
|
@@ -9,7 +9,6 @@
|
|
|
9
9
|
|
|
10
10
|
import * as React from "react";
|
|
11
11
|
|
|
12
|
-
// Fix TS2883: The inferred type cannot be named without a reference to @radix-ui/react-dialog
|
|
13
12
|
import type { } from "@radix-ui/react-dialog";
|
|
14
13
|
import { Drawer as DrawerPrimitive } from "vaul";
|
|
15
14
|
|
|
@@ -31,8 +30,6 @@ const DrawerTrigger = React.forwardRef<
|
|
|
31
30
|
));
|
|
32
31
|
DrawerTrigger.displayName = "DrawerTrigger";
|
|
33
32
|
|
|
34
|
-
|
|
35
|
-
|
|
36
33
|
const DrawerClose = React.forwardRef<
|
|
37
34
|
React.ElementRef<typeof DrawerPrimitive.Close>,
|
|
38
35
|
React.ComponentPropsWithoutRef<typeof DrawerPrimitive.Close>
|