@dowel-ui/react 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/ai-agent-status/ai-agent-status.d.ts +1 -1
- package/dist/components/ai-tool/ai-tool.d.ts +1 -1
- package/dist/components/alert/alert.d.ts +1 -1
- package/dist/components/avatar/avatar.d.ts +1 -1
- package/dist/components/badge/badge.d.ts +1 -1
- package/dist/components/button/button.d.ts +1 -1
- package/dist/components/progress/progress.d.ts +1 -1
- package/dist/components/spinner/spinner.d.ts +1 -1
- package/dist/components/toast/toast.d.ts +1 -1
- package/package.json +7 -4
- package/src/blocks/admin-users/admin-users.stories.tsx +0 -83
- package/src/blocks/admin-users/admin-users.test.tsx +0 -159
- package/src/blocks/ai-chat/ai-chat.stories.tsx +0 -149
- package/src/blocks/ai-chat/ai-chat.test.tsx +0 -166
- package/src/blocks/dashboard/dashboard.stories.tsx +0 -106
- package/src/blocks/dashboard/dashboard.test.tsx +0 -94
- package/src/blocks/forgot-password/forgot-password.stories.tsx +0 -41
- package/src/blocks/forgot-password/forgot-password.test.tsx +0 -62
- package/src/blocks/login/login.stories.tsx +0 -46
- package/src/blocks/login/login.test.tsx +0 -100
- package/src/blocks/pricing/pricing.stories.tsx +0 -62
- package/src/blocks/pricing/pricing.test.tsx +0 -101
- package/src/blocks/settings/settings.stories.tsx +0 -94
- package/src/blocks/settings/settings.test.tsx +0 -167
- package/src/blocks/signup/signup.stories.tsx +0 -27
- package/src/blocks/signup/signup.test.tsx +0 -97
- package/src/components/accordion/accordion.stories.tsx +0 -90
- package/src/components/accordion/accordion.test.tsx +0 -156
- package/src/components/activity-feed/activity-feed.stories.tsx +0 -163
- package/src/components/activity-feed/activity-feed.test.tsx +0 -125
- package/src/components/ai-agent-status/ai-agent-status.stories.tsx +0 -86
- package/src/components/ai-agent-status/ai-agent-status.test.tsx +0 -66
- package/src/components/ai-conversation/ai-conversation.stories.tsx +0 -126
- package/src/components/ai-conversation/ai-conversation.test.tsx +0 -144
- package/src/components/ai-message/ai-message.stories.tsx +0 -106
- package/src/components/ai-message/ai-message.test.tsx +0 -160
- package/src/components/ai-model-selector/ai-model-selector.stories.tsx +0 -66
- package/src/components/ai-model-selector/ai-model-selector.test.tsx +0 -120
- package/src/components/ai-prompt-input/ai-prompt-input.stories.tsx +0 -145
- package/src/components/ai-prompt-input/ai-prompt-input.test.tsx +0 -273
- package/src/components/ai-reasoning/ai-reasoning.stories.tsx +0 -60
- package/src/components/ai-reasoning/ai-reasoning.test.tsx +0 -95
- package/src/components/ai-response/ai-response.stories.tsx +0 -75
- package/src/components/ai-response/ai-response.test.tsx +0 -96
- package/src/components/ai-sources/ai-sources.stories.tsx +0 -93
- package/src/components/ai-sources/ai-sources.test.tsx +0 -102
- package/src/components/ai-token-usage/ai-token-usage.stories.tsx +0 -59
- package/src/components/ai-token-usage/ai-token-usage.test.tsx +0 -83
- package/src/components/ai-tool/ai-tool.stories.tsx +0 -96
- package/src/components/ai-tool/ai-tool.test.tsx +0 -137
- package/src/components/alert/alert.stories.tsx +0 -101
- package/src/components/alert/alert.test.tsx +0 -94
- package/src/components/avatar/avatar.stories.tsx +0 -58
- package/src/components/avatar/avatar.test.tsx +0 -64
- package/src/components/badge/badge.stories.tsx +0 -68
- package/src/components/badge/badge.test.tsx +0 -61
- package/src/components/button/button.stories.tsx +0 -128
- package/src/components/button/button.test.tsx +0 -224
- package/src/components/calendar/calendar.stories.tsx +0 -105
- package/src/components/calendar/calendar.test.tsx +0 -255
- package/src/components/card/card.stories.tsx +0 -61
- package/src/components/card/card.test.tsx +0 -66
- package/src/components/checkbox/checkbox.stories.tsx +0 -124
- package/src/components/checkbox/checkbox.test.tsx +0 -121
- package/src/components/code-block/code-block.stories.tsx +0 -62
- package/src/components/code-block/code-block.test.tsx +0 -139
- package/src/components/combobox/combobox.stories.tsx +0 -171
- package/src/components/combobox/combobox.test.tsx +0 -429
- package/src/components/command/command.stories.tsx +0 -152
- package/src/components/command/command.test.tsx +0 -380
- package/src/components/data-table/data-table.stories.tsx +0 -151
- package/src/components/data-table/data-table.test.tsx +0 -288
- package/src/components/date-picker/date-picker.stories.tsx +0 -103
- package/src/components/date-picker/date-picker.test.tsx +0 -205
- package/src/components/dialog/dialog.stories.tsx +0 -140
- package/src/components/dialog/dialog.test.tsx +0 -186
- package/src/components/drawer/drawer.stories.tsx +0 -111
- package/src/components/drawer/drawer.test.tsx +0 -187
- package/src/components/dropdown-menu/dropdown-menu.stories.tsx +0 -128
- package/src/components/dropdown-menu/dropdown-menu.test.tsx +0 -321
- package/src/components/empty-state/empty-state.stories.tsx +0 -70
- package/src/components/empty-state/empty-state.test.tsx +0 -97
- package/src/components/form/form.stories.tsx +0 -144
- package/src/components/form/form.test.tsx +0 -203
- package/src/components/input/input.stories.tsx +0 -84
- package/src/components/input/input.test.tsx +0 -108
- package/src/components/label/label.stories.tsx +0 -51
- package/src/components/label/label.test.tsx +0 -55
- package/src/components/pagination/pagination.stories.tsx +0 -129
- package/src/components/pagination/pagination.test.tsx +0 -119
- package/src/components/popover/popover.stories.tsx +0 -84
- package/src/components/popover/popover.test.tsx +0 -163
- package/src/components/progress/progress.stories.tsx +0 -90
- package/src/components/progress/progress.test.tsx +0 -78
- package/src/components/radio-group/radio-group.stories.tsx +0 -102
- package/src/components/radio-group/radio-group.test.tsx +0 -152
- package/src/components/select/select.stories.tsx +0 -137
- package/src/components/select/select.test.tsx +0 -209
- package/src/components/separator/separator.stories.tsx +0 -42
- package/src/components/separator/separator.test.tsx +0 -46
- package/src/components/sheet/sheet.stories.tsx +0 -107
- package/src/components/sheet/sheet.test.tsx +0 -152
- package/src/components/skeleton/skeleton.stories.tsx +0 -46
- package/src/components/skeleton/skeleton.test.tsx +0 -57
- package/src/components/slider/slider.stories.tsx +0 -87
- package/src/components/slider/slider.test.tsx +0 -148
- package/src/components/spinner/spinner.stories.tsx +0 -47
- package/src/components/spinner/spinner.test.tsx +0 -41
- package/src/components/switch/switch.stories.tsx +0 -70
- package/src/components/switch/switch.test.tsx +0 -101
- package/src/components/table/table.stories.tsx +0 -131
- package/src/components/table/table.test.tsx +0 -153
- package/src/components/tabs/tabs.stories.tsx +0 -139
- package/src/components/tabs/tabs.test.tsx +0 -148
- package/src/components/toast/toast.stories.tsx +0 -167
- package/src/components/toast/toast.test.tsx +0 -306
- package/src/components/tooltip/tooltip.stories.tsx +0 -89
- package/src/components/tooltip/tooltip.test.tsx +0 -120
- package/src/lib/utils.test.ts +0 -32
- package/src/registry/meta.test.ts +0 -234
|
@@ -1,101 +0,0 @@
|
|
|
1
|
-
import type { Decorator, Meta, StoryObj } from "@storybook/react-vite";
|
|
2
|
-
import { CircleAlert, CircleCheck, Info, TriangleAlert } from "lucide-react";
|
|
3
|
-
|
|
4
|
-
import { Alert, AlertDescription, AlertTitle } from "./alert";
|
|
5
|
-
|
|
6
|
-
/** Named so its type is nameable in declaration output (TS2883). */
|
|
7
|
-
const withFixedWidth: Decorator = (Story) => (
|
|
8
|
-
<div className="w-96">
|
|
9
|
-
<Story />
|
|
10
|
-
</div>
|
|
11
|
-
);
|
|
12
|
-
|
|
13
|
-
const meta = {
|
|
14
|
-
title: "Feedback/Alert",
|
|
15
|
-
component: Alert,
|
|
16
|
-
args: { variant: "default", live: "off" },
|
|
17
|
-
argTypes: {
|
|
18
|
-
variant: {
|
|
19
|
-
control: "select",
|
|
20
|
-
options: ["default", "destructive", "success", "warning", "info"],
|
|
21
|
-
},
|
|
22
|
-
live: { control: "select", options: ["off", "polite", "assertive"] },
|
|
23
|
-
},
|
|
24
|
-
decorators: [withFixedWidth],
|
|
25
|
-
} satisfies Meta<typeof Alert>;
|
|
26
|
-
|
|
27
|
-
export default meta;
|
|
28
|
-
type Story = StoryObj<typeof meta>;
|
|
29
|
-
|
|
30
|
-
export const Default: Story = {
|
|
31
|
-
render: (args) => (
|
|
32
|
-
<Alert {...args}>
|
|
33
|
-
<Info />
|
|
34
|
-
<AlertTitle>Heads up</AlertTitle>
|
|
35
|
-
<AlertDescription>Your trial ends in 3 days.</AlertDescription>
|
|
36
|
-
</Alert>
|
|
37
|
-
),
|
|
38
|
-
};
|
|
39
|
-
|
|
40
|
-
export const Variants: Story = {
|
|
41
|
-
parameters: { controls: { disable: true } },
|
|
42
|
-
render: () => (
|
|
43
|
-
<div className="space-y-3">
|
|
44
|
-
<Alert>
|
|
45
|
-
<Info />
|
|
46
|
-
<AlertTitle>Heads up</AlertTitle>
|
|
47
|
-
<AlertDescription>Your trial ends in 3 days.</AlertDescription>
|
|
48
|
-
</Alert>
|
|
49
|
-
<Alert variant="success">
|
|
50
|
-
<CircleCheck />
|
|
51
|
-
<AlertTitle>Deployment succeeded</AlertTitle>
|
|
52
|
-
<AlertDescription>Build 1420 is live in production.</AlertDescription>
|
|
53
|
-
</Alert>
|
|
54
|
-
<Alert variant="warning">
|
|
55
|
-
<TriangleAlert />
|
|
56
|
-
<AlertTitle>Approaching your quota</AlertTitle>
|
|
57
|
-
<AlertDescription>You have used 92% of your monthly builds.</AlertDescription>
|
|
58
|
-
</Alert>
|
|
59
|
-
<Alert variant="destructive">
|
|
60
|
-
<CircleAlert />
|
|
61
|
-
<AlertTitle>Payment failed</AlertTitle>
|
|
62
|
-
<AlertDescription>Update your card to keep your workspace active.</AlertDescription>
|
|
63
|
-
</Alert>
|
|
64
|
-
</div>
|
|
65
|
-
),
|
|
66
|
-
};
|
|
67
|
-
|
|
68
|
-
export const WithoutIcon: Story = {
|
|
69
|
-
parameters: { controls: { disable: true } },
|
|
70
|
-
render: () => (
|
|
71
|
-
<Alert>
|
|
72
|
-
<AlertTitle>Scheduled maintenance</AlertTitle>
|
|
73
|
-
<AlertDescription>Deployments pause on Sunday from 02:00 to 04:00 UTC.</AlertDescription>
|
|
74
|
-
</Alert>
|
|
75
|
-
),
|
|
76
|
-
};
|
|
77
|
-
|
|
78
|
-
export const TitleOnly: Story = {
|
|
79
|
-
parameters: { controls: { disable: true } },
|
|
80
|
-
render: () => (
|
|
81
|
-
<Alert variant="info">
|
|
82
|
-
<Info />
|
|
83
|
-
<AlertTitle>Two-factor authentication is now required.</AlertTitle>
|
|
84
|
-
</Alert>
|
|
85
|
-
),
|
|
86
|
-
};
|
|
87
|
-
|
|
88
|
-
/**
|
|
89
|
-
* An alert that appears in response to a user action should announce itself.
|
|
90
|
-
* The default `live="off"` avoids announcing notices that were already on screen.
|
|
91
|
-
*/
|
|
92
|
-
export const LiveRegion: Story = {
|
|
93
|
-
parameters: { controls: { disable: true } },
|
|
94
|
-
render: () => (
|
|
95
|
-
<Alert live="assertive" variant="destructive">
|
|
96
|
-
<CircleAlert />
|
|
97
|
-
<AlertTitle>Could not save changes</AlertTitle>
|
|
98
|
-
<AlertDescription>Check your connection and try again.</AlertDescription>
|
|
99
|
-
</Alert>
|
|
100
|
-
),
|
|
101
|
-
};
|
|
@@ -1,94 +0,0 @@
|
|
|
1
|
-
import { render, screen } from "@testing-library/react";
|
|
2
|
-
import { describe, expect, it } from "vitest";
|
|
3
|
-
|
|
4
|
-
import { expectNoA11yViolations } from "../../../test/a11y";
|
|
5
|
-
import { Alert, AlertDescription, AlertTitle } from "./alert";
|
|
6
|
-
|
|
7
|
-
describe("Alert", () => {
|
|
8
|
-
it("renders its title and description", () => {
|
|
9
|
-
render(
|
|
10
|
-
<Alert>
|
|
11
|
-
<AlertTitle>Heads up</AlertTitle>
|
|
12
|
-
<AlertDescription>Your trial ends in 3 days.</AlertDescription>
|
|
13
|
-
</Alert>,
|
|
14
|
-
);
|
|
15
|
-
|
|
16
|
-
expect(screen.getByText("Heads up")).toBeInTheDocument();
|
|
17
|
-
expect(screen.getByText("Your trial ends in 3 days.")).toBeInTheDocument();
|
|
18
|
-
});
|
|
19
|
-
|
|
20
|
-
it("is not a live region by default", () => {
|
|
21
|
-
render(
|
|
22
|
-
<Alert data-testid="alert">
|
|
23
|
-
<AlertTitle>Heads up</AlertTitle>
|
|
24
|
-
</Alert>,
|
|
25
|
-
);
|
|
26
|
-
|
|
27
|
-
const alert = screen.getByTestId("alert");
|
|
28
|
-
expect(alert).not.toHaveAttribute("role");
|
|
29
|
-
expect(alert).not.toHaveAttribute("aria-live");
|
|
30
|
-
expect(screen.queryByRole("alert")).not.toBeInTheDocument();
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
it("becomes a polite status region when live is polite", () => {
|
|
34
|
-
render(
|
|
35
|
-
<Alert live="polite">
|
|
36
|
-
<AlertTitle>Saved</AlertTitle>
|
|
37
|
-
</Alert>,
|
|
38
|
-
);
|
|
39
|
-
|
|
40
|
-
const alert = screen.getByRole("status");
|
|
41
|
-
expect(alert).toHaveAttribute("aria-live", "polite");
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
it("becomes an assertive alert region when live is assertive", () => {
|
|
45
|
-
render(
|
|
46
|
-
<Alert live="assertive" variant="destructive">
|
|
47
|
-
<AlertTitle>Payment failed</AlertTitle>
|
|
48
|
-
</Alert>,
|
|
49
|
-
);
|
|
50
|
-
|
|
51
|
-
const alert = screen.getByRole("alert");
|
|
52
|
-
expect(alert).toHaveAttribute("aria-live", "assertive");
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
it.each([
|
|
56
|
-
["default", "bg-card"],
|
|
57
|
-
["destructive", "bg-destructive/8"],
|
|
58
|
-
["success", "bg-success/8"],
|
|
59
|
-
["warning", "bg-warning/10"],
|
|
60
|
-
["info", "bg-info/8"],
|
|
61
|
-
] as const)("applies the %s variant", (variant, expectedClass) => {
|
|
62
|
-
render(
|
|
63
|
-
<Alert variant={variant} data-testid="alert">
|
|
64
|
-
<AlertTitle>Title</AlertTitle>
|
|
65
|
-
</Alert>,
|
|
66
|
-
);
|
|
67
|
-
expect(screen.getByTestId("alert")).toHaveClass(expectedClass);
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
it("merges a consumer className", () => {
|
|
71
|
-
render(
|
|
72
|
-
<Alert className="mt-8" data-testid="alert">
|
|
73
|
-
<AlertTitle>Title</AlertTitle>
|
|
74
|
-
</Alert>,
|
|
75
|
-
);
|
|
76
|
-
expect(screen.getByTestId("alert")).toHaveClass("mt-8");
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
it("has no accessibility violations", async () => {
|
|
80
|
-
const { container } = render(
|
|
81
|
-
<>
|
|
82
|
-
<Alert>
|
|
83
|
-
<AlertTitle>Heads up</AlertTitle>
|
|
84
|
-
<AlertDescription>Your trial ends in 3 days.</AlertDescription>
|
|
85
|
-
</Alert>
|
|
86
|
-
<Alert live="assertive" variant="destructive">
|
|
87
|
-
<AlertTitle>Payment failed</AlertTitle>
|
|
88
|
-
<AlertDescription>Update your card to continue.</AlertDescription>
|
|
89
|
-
</Alert>
|
|
90
|
-
</>,
|
|
91
|
-
);
|
|
92
|
-
await expectNoA11yViolations(container);
|
|
93
|
-
});
|
|
94
|
-
});
|
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
import type { Meta, StoryObj } from "@storybook/react-vite";
|
|
2
|
-
|
|
3
|
-
import { Avatar, AvatarFallback, AvatarImage } from "./avatar";
|
|
4
|
-
|
|
5
|
-
const meta = {
|
|
6
|
-
title: "Display/Avatar",
|
|
7
|
-
component: Avatar,
|
|
8
|
-
args: { size: "md" },
|
|
9
|
-
argTypes: { size: { control: "select", options: ["xs", "sm", "md", "lg", "xl"] } },
|
|
10
|
-
} satisfies Meta<typeof Avatar>;
|
|
11
|
-
|
|
12
|
-
export default meta;
|
|
13
|
-
type Story = StoryObj<typeof meta>;
|
|
14
|
-
|
|
15
|
-
export const Default: Story = {
|
|
16
|
-
render: (args) => (
|
|
17
|
-
<Avatar {...args}>
|
|
18
|
-
<AvatarImage src="https://i.pravatar.cc/128?img=12" alt="Ada Lovelace" />
|
|
19
|
-
<AvatarFallback>AL</AvatarFallback>
|
|
20
|
-
</Avatar>
|
|
21
|
-
),
|
|
22
|
-
};
|
|
23
|
-
|
|
24
|
-
/** The fallback is what renders when the image is missing, slow or broken. */
|
|
25
|
-
export const Fallback: Story = {
|
|
26
|
-
render: (args) => (
|
|
27
|
-
<Avatar {...args}>
|
|
28
|
-
<AvatarImage src="/does-not-exist.png" alt="Grace Hopper" />
|
|
29
|
-
<AvatarFallback>GH</AvatarFallback>
|
|
30
|
-
</Avatar>
|
|
31
|
-
),
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
export const Sizes: Story = {
|
|
35
|
-
parameters: { controls: { disable: true } },
|
|
36
|
-
render: () => (
|
|
37
|
-
<div className="flex items-center gap-3">
|
|
38
|
-
{(["xs", "sm", "md", "lg", "xl"] as const).map((size) => (
|
|
39
|
-
<Avatar key={size} size={size}>
|
|
40
|
-
<AvatarFallback>AL</AvatarFallback>
|
|
41
|
-
</Avatar>
|
|
42
|
-
))}
|
|
43
|
-
</div>
|
|
44
|
-
),
|
|
45
|
-
};
|
|
46
|
-
|
|
47
|
-
export const Group: Story = {
|
|
48
|
-
parameters: { controls: { disable: true } },
|
|
49
|
-
render: () => (
|
|
50
|
-
<div className="flex -space-x-2">
|
|
51
|
-
{["AL", "GH", "KJ", "RM"].map((initials) => (
|
|
52
|
-
<Avatar key={initials} size="sm" className="ring-2 ring-background">
|
|
53
|
-
<AvatarFallback>{initials}</AvatarFallback>
|
|
54
|
-
</Avatar>
|
|
55
|
-
))}
|
|
56
|
-
</div>
|
|
57
|
-
),
|
|
58
|
-
};
|
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
import { render, screen, waitFor } from "@testing-library/react";
|
|
2
|
-
import { describe, expect, it } from "vitest";
|
|
3
|
-
|
|
4
|
-
import { expectNoA11yViolations } from "../../../test/a11y";
|
|
5
|
-
import { Avatar, AvatarFallback, AvatarImage } from "./avatar";
|
|
6
|
-
|
|
7
|
-
describe("Avatar", () => {
|
|
8
|
-
it("shows the fallback while the image has not loaded", async () => {
|
|
9
|
-
render(
|
|
10
|
-
<Avatar>
|
|
11
|
-
<AvatarImage src="/nope.png" alt="Ada Lovelace" />
|
|
12
|
-
<AvatarFallback>AL</AvatarFallback>
|
|
13
|
-
</Avatar>,
|
|
14
|
-
);
|
|
15
|
-
|
|
16
|
-
await waitFor(() => {
|
|
17
|
-
expect(screen.getByText("AL")).toBeInTheDocument();
|
|
18
|
-
});
|
|
19
|
-
});
|
|
20
|
-
|
|
21
|
-
it("renders the fallback alone when there is no image", () => {
|
|
22
|
-
render(
|
|
23
|
-
<Avatar>
|
|
24
|
-
<AvatarFallback>AL</AvatarFallback>
|
|
25
|
-
</Avatar>,
|
|
26
|
-
);
|
|
27
|
-
expect(screen.getByText("AL")).toBeInTheDocument();
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
it.each([
|
|
31
|
-
["xs", "size-6"],
|
|
32
|
-
["sm", "size-8"],
|
|
33
|
-
["md", "size-10"],
|
|
34
|
-
["lg", "size-12"],
|
|
35
|
-
["xl", "size-16"],
|
|
36
|
-
] as const)("applies the %s size", (size, expectedClass) => {
|
|
37
|
-
render(
|
|
38
|
-
<Avatar size={size} data-testid="avatar">
|
|
39
|
-
<AvatarFallback>AL</AvatarFallback>
|
|
40
|
-
</Avatar>,
|
|
41
|
-
);
|
|
42
|
-
expect(screen.getByTestId("avatar")).toHaveClass(expectedClass);
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
it("lets a consumer className override the size", () => {
|
|
46
|
-
render(
|
|
47
|
-
<Avatar className="size-24" data-testid="avatar">
|
|
48
|
-
<AvatarFallback>AL</AvatarFallback>
|
|
49
|
-
</Avatar>,
|
|
50
|
-
);
|
|
51
|
-
const avatar = screen.getByTestId("avatar");
|
|
52
|
-
expect(avatar).toHaveClass("size-24");
|
|
53
|
-
expect(avatar).not.toHaveClass("size-10");
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
it("has no accessibility violations", async () => {
|
|
57
|
-
const { container } = render(
|
|
58
|
-
<Avatar>
|
|
59
|
-
<AvatarFallback>AL</AvatarFallback>
|
|
60
|
-
</Avatar>,
|
|
61
|
-
);
|
|
62
|
-
await expectNoA11yViolations(container);
|
|
63
|
-
});
|
|
64
|
-
});
|
|
@@ -1,68 +0,0 @@
|
|
|
1
|
-
import type { Meta, StoryObj } from "@storybook/react-vite";
|
|
2
|
-
import { Check } from "lucide-react";
|
|
3
|
-
|
|
4
|
-
import { Badge } from "./badge";
|
|
5
|
-
|
|
6
|
-
const meta = {
|
|
7
|
-
title: "Display/Badge",
|
|
8
|
-
component: Badge,
|
|
9
|
-
args: { children: "Active", variant: "default", size: "md" },
|
|
10
|
-
argTypes: {
|
|
11
|
-
variant: {
|
|
12
|
-
control: "select",
|
|
13
|
-
options: ["default", "secondary", "outline", "destructive", "success", "warning", "info"],
|
|
14
|
-
},
|
|
15
|
-
size: { control: "select", options: ["sm", "md"] },
|
|
16
|
-
asChild: { table: { disable: true } },
|
|
17
|
-
},
|
|
18
|
-
} satisfies Meta<typeof Badge>;
|
|
19
|
-
|
|
20
|
-
export default meta;
|
|
21
|
-
type Story = StoryObj<typeof meta>;
|
|
22
|
-
|
|
23
|
-
export const Default: Story = {};
|
|
24
|
-
|
|
25
|
-
export const Variants: Story = {
|
|
26
|
-
parameters: { controls: { disable: true } },
|
|
27
|
-
render: () => (
|
|
28
|
-
<div className="flex flex-wrap items-center gap-2">
|
|
29
|
-
<Badge>Default</Badge>
|
|
30
|
-
<Badge variant="secondary">Secondary</Badge>
|
|
31
|
-
<Badge variant="outline">Outline</Badge>
|
|
32
|
-
<Badge variant="destructive">Failed</Badge>
|
|
33
|
-
<Badge variant="success">Deployed</Badge>
|
|
34
|
-
<Badge variant="warning">Degraded</Badge>
|
|
35
|
-
<Badge variant="info">Queued</Badge>
|
|
36
|
-
</div>
|
|
37
|
-
),
|
|
38
|
-
};
|
|
39
|
-
|
|
40
|
-
export const Sizes: Story = {
|
|
41
|
-
parameters: { controls: { disable: true } },
|
|
42
|
-
render: () => (
|
|
43
|
-
<div className="flex items-center gap-2">
|
|
44
|
-
<Badge size="sm">Small</Badge>
|
|
45
|
-
<Badge size="md">Medium</Badge>
|
|
46
|
-
</div>
|
|
47
|
-
),
|
|
48
|
-
};
|
|
49
|
-
|
|
50
|
-
export const WithIcon: Story = {
|
|
51
|
-
parameters: { controls: { disable: true } },
|
|
52
|
-
render: () => (
|
|
53
|
-
<Badge variant="success">
|
|
54
|
-
<Check />
|
|
55
|
-
Verified
|
|
56
|
-
</Badge>
|
|
57
|
-
),
|
|
58
|
-
};
|
|
59
|
-
|
|
60
|
-
export const AsLink: Story = {
|
|
61
|
-
name: "asChild (renders a link)",
|
|
62
|
-
parameters: { controls: { disable: true } },
|
|
63
|
-
render: () => (
|
|
64
|
-
<Badge asChild variant="outline">
|
|
65
|
-
<a href="#status">View status</a>
|
|
66
|
-
</Badge>
|
|
67
|
-
),
|
|
68
|
-
};
|
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
import { render, screen } from "@testing-library/react";
|
|
2
|
-
import { describe, expect, it } from "vitest";
|
|
3
|
-
|
|
4
|
-
import { expectNoA11yViolations } from "../../../test/a11y";
|
|
5
|
-
import { Badge } from "./badge";
|
|
6
|
-
|
|
7
|
-
describe("Badge", () => {
|
|
8
|
-
it("renders its label", () => {
|
|
9
|
-
render(<Badge>Active</Badge>);
|
|
10
|
-
expect(screen.getByText("Active")).toBeInTheDocument();
|
|
11
|
-
});
|
|
12
|
-
|
|
13
|
-
it.each([
|
|
14
|
-
["default", "bg-primary"],
|
|
15
|
-
["secondary", "bg-secondary"],
|
|
16
|
-
["outline", "border-border-strong"],
|
|
17
|
-
["destructive", "bg-destructive"],
|
|
18
|
-
["success", "bg-success"],
|
|
19
|
-
["warning", "bg-warning"],
|
|
20
|
-
["info", "bg-info"],
|
|
21
|
-
] as const)("applies the %s variant", (variant, expectedClass) => {
|
|
22
|
-
render(<Badge variant={variant}>Label</Badge>);
|
|
23
|
-
expect(screen.getByText("Label")).toHaveClass(expectedClass);
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
it.each([
|
|
27
|
-
["sm", "h-5"],
|
|
28
|
-
["md", "h-6"],
|
|
29
|
-
] as const)("applies the %s size", (size, expectedClass) => {
|
|
30
|
-
render(<Badge size={size}>Label</Badge>);
|
|
31
|
-
expect(screen.getByText("Label")).toHaveClass(expectedClass);
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
it("lets a consumer className override a conflicting utility", () => {
|
|
35
|
-
render(<Badge className="bg-card">Label</Badge>);
|
|
36
|
-
const badge = screen.getByText("Label");
|
|
37
|
-
expect(badge).toHaveClass("bg-card");
|
|
38
|
-
expect(badge).not.toHaveClass("bg-primary");
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
it("renders as a link when asChild is set", () => {
|
|
42
|
-
render(
|
|
43
|
-
<Badge asChild>
|
|
44
|
-
<a href="/status">Active</a>
|
|
45
|
-
</Badge>,
|
|
46
|
-
);
|
|
47
|
-
const link = screen.getByRole("link", { name: "Active" });
|
|
48
|
-
expect(link).toHaveAttribute("href", "/status");
|
|
49
|
-
expect(link).toHaveClass("bg-primary");
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
it("has no accessibility violations", async () => {
|
|
53
|
-
const { container } = render(
|
|
54
|
-
<>
|
|
55
|
-
<Badge>Active</Badge>
|
|
56
|
-
<Badge variant="destructive">Failed</Badge>
|
|
57
|
-
</>,
|
|
58
|
-
);
|
|
59
|
-
await expectNoA11yViolations(container);
|
|
60
|
-
});
|
|
61
|
-
});
|
|
@@ -1,128 +0,0 @@
|
|
|
1
|
-
import type { Meta, StoryObj } from "@storybook/react-vite";
|
|
2
|
-
import { ArrowRight, Plus, Trash2 } from "lucide-react";
|
|
3
|
-
|
|
4
|
-
import { Button } from "./button";
|
|
5
|
-
|
|
6
|
-
const meta = {
|
|
7
|
-
title: "Foundation/Button",
|
|
8
|
-
component: Button,
|
|
9
|
-
args: {
|
|
10
|
-
children: "Continue",
|
|
11
|
-
variant: "primary",
|
|
12
|
-
size: "md",
|
|
13
|
-
},
|
|
14
|
-
argTypes: {
|
|
15
|
-
variant: {
|
|
16
|
-
control: "select",
|
|
17
|
-
options: ["primary", "secondary", "outline", "ghost", "destructive", "link"],
|
|
18
|
-
},
|
|
19
|
-
size: { control: "select", options: ["sm", "md", "lg", "icon", "icon-sm"] },
|
|
20
|
-
loading: { control: "boolean" },
|
|
21
|
-
disabled: { control: "boolean" },
|
|
22
|
-
asChild: { table: { disable: true } },
|
|
23
|
-
},
|
|
24
|
-
} satisfies Meta<typeof Button>;
|
|
25
|
-
|
|
26
|
-
export default meta;
|
|
27
|
-
type Story = StoryObj<typeof meta>;
|
|
28
|
-
|
|
29
|
-
export const Default: Story = {};
|
|
30
|
-
|
|
31
|
-
export const Variants: Story = {
|
|
32
|
-
parameters: { controls: { disable: true } },
|
|
33
|
-
render: () => (
|
|
34
|
-
<div className="flex flex-wrap items-center gap-3">
|
|
35
|
-
<Button variant="primary">Primary</Button>
|
|
36
|
-
<Button variant="secondary">Secondary</Button>
|
|
37
|
-
<Button variant="outline">Outline</Button>
|
|
38
|
-
<Button variant="ghost">Ghost</Button>
|
|
39
|
-
<Button variant="destructive">Destructive</Button>
|
|
40
|
-
<Button variant="link">Link</Button>
|
|
41
|
-
</div>
|
|
42
|
-
),
|
|
43
|
-
};
|
|
44
|
-
|
|
45
|
-
export const Sizes: Story = {
|
|
46
|
-
parameters: { controls: { disable: true } },
|
|
47
|
-
render: () => (
|
|
48
|
-
<div className="flex flex-wrap items-center gap-3">
|
|
49
|
-
<Button size="sm">Small</Button>
|
|
50
|
-
<Button size="md">Medium</Button>
|
|
51
|
-
<Button size="lg">Large</Button>
|
|
52
|
-
<Button size="icon" aria-label="Add item">
|
|
53
|
-
<Plus />
|
|
54
|
-
</Button>
|
|
55
|
-
<Button size="icon-sm" variant="outline" aria-label="Delete item">
|
|
56
|
-
<Trash2 />
|
|
57
|
-
</Button>
|
|
58
|
-
</div>
|
|
59
|
-
),
|
|
60
|
-
};
|
|
61
|
-
|
|
62
|
-
export const WithIcons: Story = {
|
|
63
|
-
parameters: { controls: { disable: true } },
|
|
64
|
-
render: () => (
|
|
65
|
-
<div className="flex flex-wrap items-center gap-3">
|
|
66
|
-
<Button>
|
|
67
|
-
<Plus />
|
|
68
|
-
New project
|
|
69
|
-
</Button>
|
|
70
|
-
<Button variant="outline">
|
|
71
|
-
Continue
|
|
72
|
-
<ArrowRight />
|
|
73
|
-
</Button>
|
|
74
|
-
</div>
|
|
75
|
-
),
|
|
76
|
-
};
|
|
77
|
-
|
|
78
|
-
export const Loading: Story = {
|
|
79
|
-
args: { loading: true },
|
|
80
|
-
};
|
|
81
|
-
|
|
82
|
-
export const LoadingAcrossVariants: Story = {
|
|
83
|
-
name: "Loading (all variants)",
|
|
84
|
-
parameters: { controls: { disable: true } },
|
|
85
|
-
render: () => (
|
|
86
|
-
<div className="flex flex-wrap items-center gap-3">
|
|
87
|
-
<Button loading>Saving</Button>
|
|
88
|
-
<Button variant="secondary" loading>
|
|
89
|
-
Saving
|
|
90
|
-
</Button>
|
|
91
|
-
<Button variant="outline" loading>
|
|
92
|
-
Saving
|
|
93
|
-
</Button>
|
|
94
|
-
<Button variant="destructive" loading>
|
|
95
|
-
Deleting
|
|
96
|
-
</Button>
|
|
97
|
-
</div>
|
|
98
|
-
),
|
|
99
|
-
};
|
|
100
|
-
|
|
101
|
-
export const Disabled: Story = {
|
|
102
|
-
args: { disabled: true },
|
|
103
|
-
};
|
|
104
|
-
|
|
105
|
-
export const AsLink: Story = {
|
|
106
|
-
name: "asChild (renders a link)",
|
|
107
|
-
parameters: { controls: { disable: true } },
|
|
108
|
-
render: () => (
|
|
109
|
-
<Button asChild>
|
|
110
|
-
<a href="#pricing">View pricing</a>
|
|
111
|
-
</Button>
|
|
112
|
-
),
|
|
113
|
-
};
|
|
114
|
-
|
|
115
|
-
/** Icon-only buttons need an accessible name; the a11y addon fails without one. */
|
|
116
|
-
export const IconOnly: Story = {
|
|
117
|
-
parameters: { controls: { disable: true } },
|
|
118
|
-
render: () => (
|
|
119
|
-
<div className="flex items-center gap-3">
|
|
120
|
-
<Button size="icon" aria-label="Add item">
|
|
121
|
-
<Plus />
|
|
122
|
-
</Button>
|
|
123
|
-
<Button size="icon" variant="outline" aria-label="Delete item">
|
|
124
|
-
<Trash2 />
|
|
125
|
-
</Button>
|
|
126
|
-
</div>
|
|
127
|
-
),
|
|
128
|
-
};
|