@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.
Files changed (120) hide show
  1. package/dist/components/ai-agent-status/ai-agent-status.d.ts +1 -1
  2. package/dist/components/ai-tool/ai-tool.d.ts +1 -1
  3. package/dist/components/alert/alert.d.ts +1 -1
  4. package/dist/components/avatar/avatar.d.ts +1 -1
  5. package/dist/components/badge/badge.d.ts +1 -1
  6. package/dist/components/button/button.d.ts +1 -1
  7. package/dist/components/progress/progress.d.ts +1 -1
  8. package/dist/components/spinner/spinner.d.ts +1 -1
  9. package/dist/components/toast/toast.d.ts +1 -1
  10. package/package.json +7 -4
  11. package/src/blocks/admin-users/admin-users.stories.tsx +0 -83
  12. package/src/blocks/admin-users/admin-users.test.tsx +0 -159
  13. package/src/blocks/ai-chat/ai-chat.stories.tsx +0 -149
  14. package/src/blocks/ai-chat/ai-chat.test.tsx +0 -166
  15. package/src/blocks/dashboard/dashboard.stories.tsx +0 -106
  16. package/src/blocks/dashboard/dashboard.test.tsx +0 -94
  17. package/src/blocks/forgot-password/forgot-password.stories.tsx +0 -41
  18. package/src/blocks/forgot-password/forgot-password.test.tsx +0 -62
  19. package/src/blocks/login/login.stories.tsx +0 -46
  20. package/src/blocks/login/login.test.tsx +0 -100
  21. package/src/blocks/pricing/pricing.stories.tsx +0 -62
  22. package/src/blocks/pricing/pricing.test.tsx +0 -101
  23. package/src/blocks/settings/settings.stories.tsx +0 -94
  24. package/src/blocks/settings/settings.test.tsx +0 -167
  25. package/src/blocks/signup/signup.stories.tsx +0 -27
  26. package/src/blocks/signup/signup.test.tsx +0 -97
  27. package/src/components/accordion/accordion.stories.tsx +0 -90
  28. package/src/components/accordion/accordion.test.tsx +0 -156
  29. package/src/components/activity-feed/activity-feed.stories.tsx +0 -163
  30. package/src/components/activity-feed/activity-feed.test.tsx +0 -125
  31. package/src/components/ai-agent-status/ai-agent-status.stories.tsx +0 -86
  32. package/src/components/ai-agent-status/ai-agent-status.test.tsx +0 -66
  33. package/src/components/ai-conversation/ai-conversation.stories.tsx +0 -126
  34. package/src/components/ai-conversation/ai-conversation.test.tsx +0 -144
  35. package/src/components/ai-message/ai-message.stories.tsx +0 -106
  36. package/src/components/ai-message/ai-message.test.tsx +0 -160
  37. package/src/components/ai-model-selector/ai-model-selector.stories.tsx +0 -66
  38. package/src/components/ai-model-selector/ai-model-selector.test.tsx +0 -120
  39. package/src/components/ai-prompt-input/ai-prompt-input.stories.tsx +0 -145
  40. package/src/components/ai-prompt-input/ai-prompt-input.test.tsx +0 -273
  41. package/src/components/ai-reasoning/ai-reasoning.stories.tsx +0 -60
  42. package/src/components/ai-reasoning/ai-reasoning.test.tsx +0 -95
  43. package/src/components/ai-response/ai-response.stories.tsx +0 -75
  44. package/src/components/ai-response/ai-response.test.tsx +0 -96
  45. package/src/components/ai-sources/ai-sources.stories.tsx +0 -93
  46. package/src/components/ai-sources/ai-sources.test.tsx +0 -102
  47. package/src/components/ai-token-usage/ai-token-usage.stories.tsx +0 -59
  48. package/src/components/ai-token-usage/ai-token-usage.test.tsx +0 -83
  49. package/src/components/ai-tool/ai-tool.stories.tsx +0 -96
  50. package/src/components/ai-tool/ai-tool.test.tsx +0 -137
  51. package/src/components/alert/alert.stories.tsx +0 -101
  52. package/src/components/alert/alert.test.tsx +0 -94
  53. package/src/components/avatar/avatar.stories.tsx +0 -58
  54. package/src/components/avatar/avatar.test.tsx +0 -64
  55. package/src/components/badge/badge.stories.tsx +0 -68
  56. package/src/components/badge/badge.test.tsx +0 -61
  57. package/src/components/button/button.stories.tsx +0 -128
  58. package/src/components/button/button.test.tsx +0 -224
  59. package/src/components/calendar/calendar.stories.tsx +0 -105
  60. package/src/components/calendar/calendar.test.tsx +0 -255
  61. package/src/components/card/card.stories.tsx +0 -61
  62. package/src/components/card/card.test.tsx +0 -66
  63. package/src/components/checkbox/checkbox.stories.tsx +0 -124
  64. package/src/components/checkbox/checkbox.test.tsx +0 -121
  65. package/src/components/code-block/code-block.stories.tsx +0 -62
  66. package/src/components/code-block/code-block.test.tsx +0 -139
  67. package/src/components/combobox/combobox.stories.tsx +0 -171
  68. package/src/components/combobox/combobox.test.tsx +0 -429
  69. package/src/components/command/command.stories.tsx +0 -152
  70. package/src/components/command/command.test.tsx +0 -380
  71. package/src/components/data-table/data-table.stories.tsx +0 -151
  72. package/src/components/data-table/data-table.test.tsx +0 -288
  73. package/src/components/date-picker/date-picker.stories.tsx +0 -103
  74. package/src/components/date-picker/date-picker.test.tsx +0 -205
  75. package/src/components/dialog/dialog.stories.tsx +0 -140
  76. package/src/components/dialog/dialog.test.tsx +0 -186
  77. package/src/components/drawer/drawer.stories.tsx +0 -111
  78. package/src/components/drawer/drawer.test.tsx +0 -187
  79. package/src/components/dropdown-menu/dropdown-menu.stories.tsx +0 -128
  80. package/src/components/dropdown-menu/dropdown-menu.test.tsx +0 -321
  81. package/src/components/empty-state/empty-state.stories.tsx +0 -70
  82. package/src/components/empty-state/empty-state.test.tsx +0 -97
  83. package/src/components/form/form.stories.tsx +0 -144
  84. package/src/components/form/form.test.tsx +0 -203
  85. package/src/components/input/input.stories.tsx +0 -84
  86. package/src/components/input/input.test.tsx +0 -108
  87. package/src/components/label/label.stories.tsx +0 -51
  88. package/src/components/label/label.test.tsx +0 -55
  89. package/src/components/pagination/pagination.stories.tsx +0 -129
  90. package/src/components/pagination/pagination.test.tsx +0 -119
  91. package/src/components/popover/popover.stories.tsx +0 -84
  92. package/src/components/popover/popover.test.tsx +0 -163
  93. package/src/components/progress/progress.stories.tsx +0 -90
  94. package/src/components/progress/progress.test.tsx +0 -78
  95. package/src/components/radio-group/radio-group.stories.tsx +0 -102
  96. package/src/components/radio-group/radio-group.test.tsx +0 -152
  97. package/src/components/select/select.stories.tsx +0 -137
  98. package/src/components/select/select.test.tsx +0 -209
  99. package/src/components/separator/separator.stories.tsx +0 -42
  100. package/src/components/separator/separator.test.tsx +0 -46
  101. package/src/components/sheet/sheet.stories.tsx +0 -107
  102. package/src/components/sheet/sheet.test.tsx +0 -152
  103. package/src/components/skeleton/skeleton.stories.tsx +0 -46
  104. package/src/components/skeleton/skeleton.test.tsx +0 -57
  105. package/src/components/slider/slider.stories.tsx +0 -87
  106. package/src/components/slider/slider.test.tsx +0 -148
  107. package/src/components/spinner/spinner.stories.tsx +0 -47
  108. package/src/components/spinner/spinner.test.tsx +0 -41
  109. package/src/components/switch/switch.stories.tsx +0 -70
  110. package/src/components/switch/switch.test.tsx +0 -101
  111. package/src/components/table/table.stories.tsx +0 -131
  112. package/src/components/table/table.test.tsx +0 -153
  113. package/src/components/tabs/tabs.stories.tsx +0 -139
  114. package/src/components/tabs/tabs.test.tsx +0 -148
  115. package/src/components/toast/toast.stories.tsx +0 -167
  116. package/src/components/toast/toast.test.tsx +0 -306
  117. package/src/components/tooltip/tooltip.stories.tsx +0 -89
  118. package/src/components/tooltip/tooltip.test.tsx +0 -120
  119. package/src/lib/utils.test.ts +0 -32
  120. package/src/registry/meta.test.ts +0 -234
@@ -1,70 +0,0 @@
1
- import type { Meta, StoryObj } from "@storybook/react-vite";
2
-
3
- import { Label } from "@/components/label";
4
-
5
- import { Switch } from "./switch";
6
-
7
- const meta = {
8
- title: "Forms/Switch",
9
- component: Switch,
10
- args: { disabled: false },
11
- argTypes: { disabled: { control: "boolean" } },
12
- } satisfies Meta<typeof Switch>;
13
-
14
- export default meta;
15
- type Story = StoryObj<typeof meta>;
16
-
17
- export const Default: Story = {
18
- render: (args) => (
19
- <div className="flex items-center gap-2">
20
- <Switch id="airplane" {...args} />
21
- <Label htmlFor="airplane">Airplane mode</Label>
22
- </div>
23
- ),
24
- };
25
-
26
- export const States: Story = {
27
- parameters: { controls: { disable: true } },
28
- render: () => (
29
- <div className="grid gap-3">
30
- {[
31
- { id: "off", label: "Off", checked: false },
32
- { id: "on", label: "On", checked: true },
33
- ].map((state) => (
34
- <div key={state.id} className="flex items-center gap-2">
35
- <Switch id={state.id} checked={state.checked} />
36
- <Label htmlFor={state.id}>{state.label}</Label>
37
- </div>
38
- ))}
39
- <div className="flex items-center gap-2">
40
- <Switch id="disabled-off" disabled />
41
- <Label htmlFor="disabled-off">Disabled</Label>
42
- </div>
43
- <div className="flex items-center gap-2">
44
- <Switch id="disabled-on" disabled checked />
45
- <Label htmlFor="disabled-on">Disabled and on</Label>
46
- </div>
47
- </div>
48
- ),
49
- };
50
-
51
- /** A switch applies immediately — if it needs a Save button, use a Checkbox. */
52
- export const SettingsRow: Story = {
53
- parameters: { controls: { disable: true } },
54
- render: () => (
55
- <div className="w-80 divide-y divide-border rounded-lg border border-border">
56
- {[
57
- { id: "row-1", label: "Automatic deploys", hint: "Deploy every push to main." },
58
- { id: "row-2", label: "Preview comments", hint: "Comment on pull requests." },
59
- ].map((row) => (
60
- <div key={row.id} className="flex items-start justify-between gap-4 p-4">
61
- <div className="grid gap-1">
62
- <Label htmlFor={row.id}>{row.label}</Label>
63
- <p className="text-xs text-muted-foreground">{row.hint}</p>
64
- </div>
65
- <Switch id={row.id} defaultChecked={row.id === "row-1"} />
66
- </div>
67
- ))}
68
- </div>
69
- ),
70
- };
@@ -1,101 +0,0 @@
1
- import { render, screen } from "@testing-library/react";
2
- import userEvent from "@testing-library/user-event";
3
- import { useState } from "react";
4
- import { describe, expect, it, vi } from "vitest";
5
-
6
- import { expectNoA11yViolations } from "../../../test/a11y";
7
- import { Label } from "../label";
8
- import { Switch } from "./switch";
9
-
10
- describe("Switch", () => {
11
- it("is exposed as a switch, not a checkbox", () => {
12
- render(<Switch aria-label="Airplane mode" />);
13
- expect(screen.getByRole("switch", { name: "Airplane mode" })).toBeInTheDocument();
14
- });
15
-
16
- it("starts off", () => {
17
- render(<Switch aria-label="Airplane mode" />);
18
- expect(screen.getByRole("switch")).not.toBeChecked();
19
- });
20
-
21
- it("toggles on click", async () => {
22
- const user = userEvent.setup();
23
- render(<Switch aria-label="Airplane mode" />);
24
-
25
- const control = screen.getByRole("switch");
26
- await user.click(control);
27
- expect(control).toBeChecked();
28
- });
29
-
30
- it("toggles from the keyboard", async () => {
31
- const user = userEvent.setup();
32
- render(<Switch aria-label="Airplane mode" />);
33
-
34
- await user.tab();
35
- expect(screen.getByRole("switch")).toHaveFocus();
36
- await user.keyboard(" ");
37
- expect(screen.getByRole("switch")).toBeChecked();
38
- });
39
-
40
- it("reports changes", async () => {
41
- const onCheckedChange = vi.fn();
42
- const user = userEvent.setup();
43
- render(<Switch aria-label="Airplane mode" onCheckedChange={onCheckedChange} />);
44
-
45
- await user.click(screen.getByRole("switch"));
46
- expect(onCheckedChange).toHaveBeenCalledWith(true);
47
- });
48
-
49
- it("works controlled", async () => {
50
- const user = userEvent.setup();
51
-
52
- function Controlled() {
53
- const [on, setOn] = useState(false);
54
- return <Switch aria-label="Airplane mode" checked={on} onCheckedChange={setOn} />;
55
- }
56
-
57
- render(<Controlled />);
58
- await user.click(screen.getByRole("switch"));
59
- expect(screen.getByRole("switch")).toBeChecked();
60
- });
61
-
62
- it("does not toggle while disabled", async () => {
63
- const onCheckedChange = vi.fn();
64
- const user = userEvent.setup();
65
- render(<Switch aria-label="Airplane mode" disabled onCheckedChange={onCheckedChange} />);
66
-
67
- await user.click(screen.getByRole("switch"));
68
- expect(onCheckedChange).not.toHaveBeenCalled();
69
- });
70
-
71
- it("moves its thumb to reflect state, not colour alone", async () => {
72
- const user = userEvent.setup();
73
- const { container } = render(<Switch aria-label="Airplane mode" />);
74
-
75
- const thumb = container.querySelector("[data-slot='switch-thumb']");
76
- expect(thumb).toHaveAttribute("data-state", "unchecked");
77
-
78
- await user.click(screen.getByRole("switch"));
79
- expect(thumb).toHaveAttribute("data-state", "checked");
80
- });
81
-
82
- it("takes its name from an associated label", () => {
83
- render(
84
- <>
85
- <Switch id="wifi" />
86
- <Label htmlFor="wifi">Wi-Fi</Label>
87
- </>,
88
- );
89
- expect(screen.getByRole("switch", { name: "Wi-Fi" })).toBeInTheDocument();
90
- });
91
-
92
- it("has no accessibility violations", async () => {
93
- const { container } = render(
94
- <div className="flex items-center gap-2">
95
- <Switch id="a11y-wifi" />
96
- <Label htmlFor="a11y-wifi">Wi-Fi</Label>
97
- </div>,
98
- );
99
- await expectNoA11yViolations(container);
100
- });
101
- });
@@ -1,131 +0,0 @@
1
- import type { Meta, StoryObj } from "@storybook/react-vite";
2
-
3
- import { Badge } from "@/components/badge";
4
-
5
- import {
6
- Table,
7
- TableBody,
8
- TableCaption,
9
- TableCell,
10
- TableFooter,
11
- TableHead,
12
- TableHeader,
13
- TableRow,
14
- } from "./table";
15
-
16
- const INVOICES = [
17
- { id: "INV-001", status: "Paid", method: "Credit card", amount: "$250.00" },
18
- { id: "INV-002", status: "Pending", method: "PayPal", amount: "$150.00" },
19
- { id: "INV-003", status: "Failed", method: "Bank transfer", amount: "$350.00" },
20
- { id: "INV-004", status: "Paid", method: "Credit card", amount: "$450.00" },
21
- ];
22
-
23
- const meta = {
24
- title: "Data/Table",
25
- component: Table,
26
- parameters: { controls: { disable: true } },
27
- } satisfies Meta<typeof Table>;
28
-
29
- export default meta;
30
- type Story = StoryObj<typeof meta>;
31
-
32
- export const Default: Story = {
33
- render: () => (
34
- <Table className="w-[36rem]">
35
- <TableCaption>Invoices from the last month.</TableCaption>
36
- <TableHeader>
37
- <TableRow>
38
- <TableHead>Invoice</TableHead>
39
- <TableHead>Status</TableHead>
40
- <TableHead>Method</TableHead>
41
- <TableHead className="text-right">Amount</TableHead>
42
- </TableRow>
43
- </TableHeader>
44
- <TableBody>
45
- {INVOICES.map((invoice) => (
46
- <TableRow key={invoice.id}>
47
- {/* A row header, so a screen reader can say which row a cell is in. */}
48
- <TableHead scope="row" className="font-medium text-foreground">
49
- {invoice.id}
50
- </TableHead>
51
- <TableCell>
52
- <Badge
53
- variant={
54
- invoice.status === "Paid"
55
- ? "success"
56
- : invoice.status === "Failed"
57
- ? "destructive"
58
- : "secondary"
59
- }
60
- size="sm"
61
- >
62
- {invoice.status}
63
- </Badge>
64
- </TableCell>
65
- <TableCell>{invoice.method}</TableCell>
66
- <TableCell className="text-right tabular-nums">{invoice.amount}</TableCell>
67
- </TableRow>
68
- ))}
69
- </TableBody>
70
- <TableFooter>
71
- <TableRow>
72
- <TableCell colSpan={3}>Total</TableCell>
73
- <TableCell className="text-right tabular-nums">$1,200.00</TableCell>
74
- </TableRow>
75
- </TableFooter>
76
- </Table>
77
- ),
78
- };
79
-
80
- export const Minimal: Story = {
81
- render: () => (
82
- <Table className="w-96" aria-label="Team members">
83
- <TableHeader>
84
- <TableRow>
85
- <TableHead>Name</TableHead>
86
- <TableHead>Role</TableHead>
87
- </TableRow>
88
- </TableHeader>
89
- <TableBody>
90
- <TableRow>
91
- <TableCell>Ada Lovelace</TableCell>
92
- <TableCell>Engineering</TableCell>
93
- </TableRow>
94
- <TableRow>
95
- <TableCell>Grace Hopper</TableCell>
96
- <TableCell>Engineering</TableCell>
97
- </TableRow>
98
- </TableBody>
99
- </Table>
100
- ),
101
- };
102
-
103
- /** Overflowing horizontally: the wrapper is focusable so it can be scrolled by keyboard. */
104
- export const Overflowing: Story = {
105
- render: () => (
106
- <div className="w-80">
107
- <Table aria-label="Wide data">
108
- <TableHeader>
109
- <TableRow>
110
- {Array.from({ length: 8 }, (_, index) => (
111
- <TableHead key={index} className="whitespace-nowrap">
112
- Column {index + 1}
113
- </TableHead>
114
- ))}
115
- </TableRow>
116
- </TableHeader>
117
- <TableBody>
118
- {[0, 1, 2].map((row) => (
119
- <TableRow key={row}>
120
- {Array.from({ length: 8 }, (_, index) => (
121
- <TableCell key={index} className="whitespace-nowrap">
122
- Value {row + 1}.{index + 1}
123
- </TableCell>
124
- ))}
125
- </TableRow>
126
- ))}
127
- </TableBody>
128
- </Table>
129
- </div>
130
- ),
131
- };
@@ -1,153 +0,0 @@
1
- import { render, screen, within } from "@testing-library/react";
2
- import userEvent from "@testing-library/user-event";
3
- import { describe, expect, it } from "vitest";
4
-
5
- import { expectNoA11yViolations } from "../../../test/a11y";
6
- import {
7
- Table,
8
- TableBody,
9
- TableCaption,
10
- TableCell,
11
- TableFooter,
12
- TableHead,
13
- TableHeader,
14
- TableRow,
15
- } from "./table";
16
-
17
- function Example() {
18
- return (
19
- <Table>
20
- <TableCaption>Recent invoices</TableCaption>
21
- <TableHeader>
22
- <TableRow>
23
- <TableHead>Invoice</TableHead>
24
- <TableHead>Status</TableHead>
25
- <TableHead>Amount</TableHead>
26
- </TableRow>
27
- </TableHeader>
28
- <TableBody>
29
- <TableRow>
30
- <TableHead scope="row">INV-001</TableHead>
31
- <TableCell>Paid</TableCell>
32
- <TableCell>$250.00</TableCell>
33
- </TableRow>
34
- <TableRow data-state="selected">
35
- <TableHead scope="row">INV-002</TableHead>
36
- <TableCell>Pending</TableCell>
37
- <TableCell>$150.00</TableCell>
38
- </TableRow>
39
- </TableBody>
40
- <TableFooter>
41
- <TableRow>
42
- <TableCell colSpan={2}>Total</TableCell>
43
- <TableCell>$400.00</TableCell>
44
- </TableRow>
45
- </TableFooter>
46
- </Table>
47
- );
48
- }
49
-
50
- describe("Table", () => {
51
- it("renders a real table, not a grid of divs", () => {
52
- render(<Example />);
53
- expect(screen.getByRole("table")).toBeInTheDocument();
54
- });
55
-
56
- it("is named by its caption", () => {
57
- render(<Example />);
58
- expect(screen.getByRole("table", { name: "Recent invoices" })).toBeInTheDocument();
59
- });
60
-
61
- it("exposes column headers", () => {
62
- render(<Example />);
63
- const headers = screen.getAllByRole("columnheader");
64
- expect(headers.map((header) => header.textContent)).toEqual([
65
- "Invoice",
66
- "Status",
67
- "Amount",
68
- ]);
69
- });
70
-
71
- it("exposes row headers when scope is set", () => {
72
- render(<Example />);
73
- expect(screen.getAllByRole("rowheader")).toHaveLength(2);
74
- });
75
-
76
- it("groups rows into header, body and footer", () => {
77
- render(<Example />);
78
- expect(screen.getAllByRole("rowgroup")).toHaveLength(3);
79
- });
80
-
81
- it("marks a selected row for styling", () => {
82
- render(<Example />);
83
- const rows = screen.getAllByRole("row");
84
- const selected = rows.find((row) => row.getAttribute("data-state") === "selected");
85
- expect(selected).toBeDefined();
86
- expect(within(selected as HTMLElement).getByText("Pending")).toBeInTheDocument();
87
- });
88
-
89
- describe("the scrolling wrapper", () => {
90
- it("is focusable, so an overflowing table can be scrolled by keyboard", async () => {
91
- const user = userEvent.setup();
92
- const { container } = render(<Example />);
93
-
94
- const wrapper = container.querySelector("[data-slot='table-container']");
95
- expect(wrapper).toHaveAttribute("tabindex", "0");
96
-
97
- await user.tab();
98
- expect(wrapper).toHaveFocus();
99
- });
100
-
101
- it("is a named region, so it is not an unlabelled focus stop", () => {
102
- render(<Example />);
103
- expect(screen.getByRole("region", { name: "Table" })).toBeInTheDocument();
104
- });
105
-
106
- it("takes its name from the table's aria-label", () => {
107
- render(
108
- <Table aria-label="Invoices">
109
- <TableBody>
110
- <TableRow>
111
- <TableCell>Cell</TableCell>
112
- </TableRow>
113
- </TableBody>
114
- </Table>,
115
- );
116
- expect(screen.getByRole("region", { name: "Invoices" })).toBeInTheDocument();
117
- });
118
-
119
- it("accepts props of its own", () => {
120
- const { container } = render(
121
- <Table containerProps={{ className: "max-h-64", "data-testid": "wrapper" }}>
122
- <TableBody>
123
- <TableRow>
124
- <TableCell>Cell</TableCell>
125
- </TableRow>
126
- </TableBody>
127
- </Table>,
128
- );
129
- expect(container.querySelector("[data-testid='wrapper']")).toHaveClass("max-h-64");
130
- });
131
- });
132
-
133
- it("lets a consumer className override a conflicting utility", () => {
134
- render(
135
- <Table className="text-base" aria-label="T">
136
- <TableBody>
137
- <TableRow>
138
- <TableCell>Cell</TableCell>
139
- </TableRow>
140
- </TableBody>
141
- </Table>,
142
- );
143
-
144
- const table = screen.getByRole("table");
145
- expect(table).toHaveClass("text-base");
146
- expect(table).not.toHaveClass("text-sm");
147
- });
148
-
149
- it("has no accessibility violations", async () => {
150
- const { container } = render(<Example />);
151
- await expectNoA11yViolations(container);
152
- });
153
- });
@@ -1,139 +0,0 @@
1
- import type { Meta, StoryObj } from "@storybook/react-vite";
2
-
3
- import { Button } from "@/components/button";
4
- import {
5
- Card,
6
- CardContent,
7
- CardDescription,
8
- CardFooter,
9
- CardHeader,
10
- CardTitle,
11
- } from "@/components/card";
12
- import { Input } from "@/components/input";
13
- import { Label } from "@/components/label";
14
-
15
- import { Tabs, TabsContent, TabsList, TabsTrigger } from "./tabs";
16
-
17
- /**
18
- * Annotated rather than inferred with `satisfies`. These components are direct
19
- * re-exports of Radix primitives, and inferring the meta type makes the emitted
20
- * declaration reference Radix-internal prop types that are not nameable from
21
- * this path (TS2883).
22
- */
23
- const meta: Meta<typeof Tabs> = {
24
- title: "Navigation/Tabs",
25
- component: Tabs,
26
- parameters: { controls: { disable: true } },
27
- };
28
-
29
- export default meta;
30
- type Story = StoryObj<typeof Tabs>;
31
-
32
- export const Default: Story = {
33
- render: () => (
34
- <Tabs defaultValue="account" className="w-96">
35
- <TabsList>
36
- <TabsTrigger value="account">Account</TabsTrigger>
37
- <TabsTrigger value="password">Password</TabsTrigger>
38
- </TabsList>
39
- <TabsContent value="account">
40
- <Card>
41
- <CardHeader>
42
- <CardTitle>Account</CardTitle>
43
- <CardDescription>Update your account details.</CardDescription>
44
- </CardHeader>
45
- <CardContent className="grid gap-2">
46
- <Label htmlFor="tabs-name">Name</Label>
47
- <Input id="tabs-name" defaultValue="Ada Lovelace" />
48
- </CardContent>
49
- <CardFooter className="justify-end">
50
- <Button>Save</Button>
51
- </CardFooter>
52
- </Card>
53
- </TabsContent>
54
- <TabsContent value="password">
55
- <Card>
56
- <CardHeader>
57
- <CardTitle>Password</CardTitle>
58
- <CardDescription>Change your password here.</CardDescription>
59
- </CardHeader>
60
- <CardContent className="grid gap-2">
61
- <Label htmlFor="tabs-password">New password</Label>
62
- <Input id="tabs-password" type="password" />
63
- </CardContent>
64
- <CardFooter className="justify-end">
65
- <Button>Update password</Button>
66
- </CardFooter>
67
- </Card>
68
- </TabsContent>
69
- </Tabs>
70
- ),
71
- };
72
-
73
- export const Underline: Story = {
74
- render: () => (
75
- <Tabs defaultValue="overview" className="w-96">
76
- <TabsList variant="underline">
77
- <TabsTrigger value="overview" variant="underline">
78
- Overview
79
- </TabsTrigger>
80
- <TabsTrigger value="deployments" variant="underline">
81
- Deployments
82
- </TabsTrigger>
83
- <TabsTrigger value="settings" variant="underline">
84
- Settings
85
- </TabsTrigger>
86
- </TabsList>
87
- <TabsContent value="overview" className="text-sm text-muted-foreground">
88
- Traffic, errors and latency for the last 24 hours.
89
- </TabsContent>
90
- <TabsContent value="deployments" className="text-sm text-muted-foreground">
91
- 42 deployments this week.
92
- </TabsContent>
93
- <TabsContent value="settings" className="text-sm text-muted-foreground">
94
- Environment variables and build settings.
95
- </TabsContent>
96
- </Tabs>
97
- ),
98
- };
99
-
100
- export const WithDisabledTab: Story = {
101
- render: () => (
102
- <Tabs defaultValue="general" className="w-96">
103
- <TabsList>
104
- <TabsTrigger value="general">General</TabsTrigger>
105
- <TabsTrigger value="team">Team</TabsTrigger>
106
- <TabsTrigger value="billing" disabled>
107
- Billing
108
- </TabsTrigger>
109
- </TabsList>
110
- <TabsContent value="general" className="text-sm text-muted-foreground">
111
- General settings.
112
- </TabsContent>
113
- <TabsContent value="team" className="text-sm text-muted-foreground">
114
- Invite and manage teammates.
115
- </TabsContent>
116
- <TabsContent value="billing" className="text-sm text-muted-foreground">
117
- Upgrade to manage billing.
118
- </TabsContent>
119
- </Tabs>
120
- ),
121
- };
122
-
123
- /** Manual activation stops arrow keys from loading each panel in passing. */
124
- export const ManualActivation: Story = {
125
- render: () => (
126
- <Tabs defaultValue="one" activationMode="manual" className="w-96">
127
- <TabsList>
128
- <TabsTrigger value="one">Expensive one</TabsTrigger>
129
- <TabsTrigger value="two">Expensive two</TabsTrigger>
130
- </TabsList>
131
- <TabsContent value="one" className="text-sm text-muted-foreground">
132
- Arrow to the next tab, then press Enter or Space to load it.
133
- </TabsContent>
134
- <TabsContent value="two" className="text-sm text-muted-foreground">
135
- Loaded on purpose, not in passing.
136
- </TabsContent>
137
- </Tabs>
138
- ),
139
- };