@hilum/ui 0.1.1 → 0.1.3

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 (92) hide show
  1. package/README.md +23 -4
  2. package/dist/chunk-J2QQDUF4.js +204 -0
  3. package/dist/chunk-J2QQDUF4.js.map +1 -0
  4. package/dist/create-theme.d.ts +27 -0
  5. package/dist/create-theme.js +172 -0
  6. package/dist/create-theme.js.map +1 -0
  7. package/dist/index.d.ts +21 -8
  8. package/dist/index.js +393 -381
  9. package/dist/index.js.map +1 -1
  10. package/dist/tokens.css +59 -50
  11. package/dist/tokens.d.ts +46 -38
  12. package/dist/tokens.js +1 -191
  13. package/dist/tokens.js.map +1 -1
  14. package/llms.txt +1514 -0
  15. package/package.json +11 -7
  16. package/src/components/accordion.tsx +5 -5
  17. package/src/components/action-panel.tsx +6 -6
  18. package/src/components/activity-feed.tsx +4 -4
  19. package/src/components/alert-dialog.tsx +3 -3
  20. package/src/components/alert.tsx +4 -4
  21. package/src/components/avatar-stack.tsx +2 -2
  22. package/src/components/avatar.tsx +2 -2
  23. package/src/components/badge.tsx +6 -5
  24. package/src/components/breadcrumb.tsx +5 -5
  25. package/src/components/button-group.tsx +4 -4
  26. package/src/components/button.tsx +6 -5
  27. package/src/components/calendar.tsx +7 -7
  28. package/src/components/card-heading.tsx +3 -3
  29. package/src/components/card.tsx +3 -3
  30. package/src/components/chart.tsx +11 -11
  31. package/src/components/checkbox.tsx +1 -1
  32. package/src/components/color-input.tsx +7 -7
  33. package/src/components/color-picker.tsx +5 -5
  34. package/src/components/combobox.tsx +10 -10
  35. package/src/components/command-palette.tsx +11 -11
  36. package/src/components/command.tsx +13 -13
  37. package/src/components/context-menu.tsx +15 -16
  38. package/src/components/data-table.tsx +6 -6
  39. package/src/components/date-picker.tsx +9 -4
  40. package/src/components/description-list.tsx +8 -8
  41. package/src/components/dialog.tsx +3 -3
  42. package/src/components/drawer.tsx +5 -5
  43. package/src/components/dropdown-menu.tsx +15 -16
  44. package/src/components/empty-state.tsx +3 -3
  45. package/src/components/field.tsx +13 -6
  46. package/src/components/grid-list.tsx +12 -12
  47. package/src/components/hover-card.tsx +1 -1
  48. package/src/components/input-group.tsx +8 -8
  49. package/src/components/input-number.tsx +7 -7
  50. package/src/components/input-otp.tsx +3 -3
  51. package/src/components/input.tsx +5 -4
  52. package/src/components/kbd.tsx +1 -1
  53. package/src/components/label.tsx +1 -1
  54. package/src/components/menubar.tsx +21 -22
  55. package/src/components/native-select.tsx +4 -4
  56. package/src/components/navigation-menu.tsx +10 -10
  57. package/src/components/notification.tsx +10 -10
  58. package/src/components/page-heading.tsx +8 -8
  59. package/src/components/pagination.tsx +1 -1
  60. package/src/components/progress.tsx +1 -1
  61. package/src/components/property-row.tsx +1 -1
  62. package/src/components/radio-card.tsx +6 -6
  63. package/src/components/radio-group.tsx +3 -3
  64. package/src/components/resizable.tsx +3 -3
  65. package/src/components/scroll-area.tsx +1 -1
  66. package/src/components/section-heading.tsx +3 -3
  67. package/src/components/select.tsx +11 -11
  68. package/src/components/separator.tsx +1 -1
  69. package/src/components/sheet.tsx +3 -3
  70. package/src/components/sidebar.tsx +19 -19
  71. package/src/components/skeleton.tsx +1 -1
  72. package/src/components/slider.tsx +1 -1
  73. package/src/components/sonner.tsx +5 -5
  74. package/src/components/stacked-list.tsx +4 -4
  75. package/src/components/stat-card.tsx +6 -6
  76. package/src/components/steps.tsx +13 -12
  77. package/src/components/switch.tsx +1 -1
  78. package/src/components/table.tsx +9 -9
  79. package/src/components/tabs.tsx +2 -2
  80. package/src/components/textarea.tsx +4 -4
  81. package/src/components/toggle.tsx +8 -8
  82. package/src/components/tooltip.tsx +1 -1
  83. package/src/index.ts +3 -2
  84. package/src/tokens/tokens.ts +54 -40
  85. package/src/components/__tests__/data.test.tsx +0 -180
  86. package/src/components/__tests__/disclosure.test.tsx +0 -198
  87. package/src/components/__tests__/display.test.tsx +0 -262
  88. package/src/components/__tests__/feedback.test.tsx +0 -138
  89. package/src/components/__tests__/form.test.tsx +0 -297
  90. package/src/components/__tests__/layout.test.tsx +0 -388
  91. package/src/components/__tests__/overlay.test.tsx +0 -90
  92. package/src/components/__tests__/primitives.test.tsx +0 -248
@@ -1,297 +0,0 @@
1
- import { describe, it, expect, vi } from "vitest";
2
- import { render, screen, fireEvent } from "@testing-library/react";
3
- import { userEvent } from "@testing-library/user-event";
4
- import { Checkbox } from "../checkbox";
5
- import { RadioGroup, RadioGroupItem } from "../radio-group";
6
- import { Switch } from "../switch";
7
- import { Slider } from "../slider";
8
- import { InputNumber } from "../input-number";
9
- import { InputGroup } from "../input-group";
10
- import { Field } from "../field";
11
- import { NativeSelect, NativeSelectOption } from "../native-select";
12
- import { RadioCards } from "../radio-card";
13
-
14
- /* ------------------------------------------------------------------ */
15
- /* Checkbox */
16
- /* ------------------------------------------------------------------ */
17
-
18
- describe("Checkbox", () => {
19
- it("renders a checkbox button", () => {
20
- render(<Checkbox />);
21
- expect(screen.getByRole("checkbox")).toBeInTheDocument();
22
- });
23
-
24
- it("is unchecked by default", () => {
25
- render(<Checkbox />);
26
- expect(screen.getByRole("checkbox")).not.toBeChecked();
27
- });
28
-
29
- it("is disabled when disabled prop is set", () => {
30
- render(<Checkbox disabled />);
31
- expect(screen.getByRole("checkbox")).toBeDisabled();
32
- });
33
- });
34
-
35
- /* ------------------------------------------------------------------ */
36
- /* RadioGroup */
37
- /* ------------------------------------------------------------------ */
38
-
39
- describe("RadioGroup", () => {
40
- it("renders radio buttons", () => {
41
- render(
42
- <RadioGroup defaultValue="a">
43
- <RadioGroupItem value="a" id="a" />
44
- <RadioGroupItem value="b" id="b" />
45
- </RadioGroup>,
46
- );
47
- expect(screen.getAllByRole("radio")).toHaveLength(2);
48
- });
49
-
50
- it("marks default value as checked", () => {
51
- render(
52
- <RadioGroup defaultValue="first">
53
- <RadioGroupItem value="first" id="first" />
54
- <RadioGroupItem value="second" id="second" />
55
- </RadioGroup>,
56
- );
57
- const radios = screen.getAllByRole("radio");
58
- expect(radios[0]).toBeChecked();
59
- expect(radios[1]).not.toBeChecked();
60
- });
61
- });
62
-
63
- /* ------------------------------------------------------------------ */
64
- /* Switch */
65
- /* ------------------------------------------------------------------ */
66
-
67
- describe("Switch", () => {
68
- it("renders a switch button", () => {
69
- render(<Switch />);
70
- expect(screen.getByRole("switch")).toBeInTheDocument();
71
- });
72
-
73
- it("is off by default", () => {
74
- render(<Switch />);
75
- expect(screen.getByRole("switch")).toHaveAttribute("data-state", "unchecked");
76
- });
77
-
78
- it("toggles on click", async () => {
79
- const user = userEvent.setup();
80
- render(<Switch />);
81
- const sw = screen.getByRole("switch");
82
- await user.click(sw);
83
- expect(sw).toHaveAttribute("data-state", "checked");
84
- });
85
- });
86
-
87
- /* ------------------------------------------------------------------ */
88
- /* Slider */
89
- /* ------------------------------------------------------------------ */
90
-
91
- describe("Slider", () => {
92
- it("renders a slider", () => {
93
- render(<Slider defaultValue={[50]} />);
94
- expect(screen.getByRole("slider")).toBeInTheDocument();
95
- });
96
-
97
- it("shows the provided value", () => {
98
- render(<Slider defaultValue={[25]} min={0} max={100} />);
99
- expect(screen.getByRole("slider")).toHaveAttribute("aria-valuenow", "25");
100
- });
101
- });
102
-
103
- /* ------------------------------------------------------------------ */
104
- /* InputNumber */
105
- /* ------------------------------------------------------------------ */
106
-
107
- describe("InputNumber", () => {
108
- it("renders an input with the given value", () => {
109
- render(<InputNumber value={42} onChange={() => {}} />);
110
- expect(screen.getByRole("textbox")).toHaveValue("42");
111
- });
112
-
113
- it("displays unit suffix", () => {
114
- render(<InputNumber value={100} onChange={() => {}} unit="px" />);
115
- expect(screen.getByText("px")).toBeInTheDocument();
116
- });
117
-
118
- it("calls onChange with incremented value when increment button clicked", async () => {
119
- const user = userEvent.setup();
120
- const onChange = vi.fn();
121
- render(<InputNumber value={10} onChange={onChange} step={1} />);
122
- await user.click(screen.getByRole("button", { name: /increment/i }));
123
- expect(onChange).toHaveBeenCalledWith(11);
124
- });
125
-
126
- it("calls onChange with decremented value when decrement button clicked", async () => {
127
- const user = userEvent.setup();
128
- const onChange = vi.fn();
129
- render(<InputNumber value={10} onChange={onChange} step={1} />);
130
- await user.click(screen.getByRole("button", { name: /decrement/i }));
131
- expect(onChange).toHaveBeenCalledWith(9);
132
- });
133
-
134
- it("clamps to max on increment", async () => {
135
- const user = userEvent.setup();
136
- const onChange = vi.fn();
137
- render(<InputNumber value={10} onChange={onChange} max={10} />);
138
- await user.click(screen.getByRole("button", { name: /increment/i }));
139
- expect(onChange).toHaveBeenCalledWith(10);
140
- });
141
-
142
- it("hides steppers when hideSteppers is true", () => {
143
- render(<InputNumber value={5} onChange={() => {}} hideSteppers />);
144
- expect(screen.queryByRole("button", { name: /increment/i })).not.toBeInTheDocument();
145
- });
146
-
147
- it("renders with precision", () => {
148
- render(<InputNumber value={3.14} onChange={() => {}} precision={2} />);
149
- expect(screen.getByRole("textbox")).toHaveValue("3.14");
150
- });
151
- });
152
-
153
- /* ------------------------------------------------------------------ */
154
- /* InputGroup */
155
- /* ------------------------------------------------------------------ */
156
-
157
- describe("InputGroup", () => {
158
- it("renders a plain input when no addons", () => {
159
- render(<InputGroup placeholder="Enter text" />);
160
- expect(screen.getByRole("textbox")).toBeInTheDocument();
161
- });
162
-
163
- it("renders leading addon text", () => {
164
- render(<InputGroup leadingAddon="https://" placeholder="domain.com" />);
165
- expect(screen.getByText("https://")).toBeInTheDocument();
166
- });
167
-
168
- it("renders trailing addon text", () => {
169
- render(<InputGroup trailingAddon=".com" placeholder="domain" />);
170
- expect(screen.getByText(".com")).toBeInTheDocument();
171
- });
172
-
173
- it("renders error state", () => {
174
- const { container } = render(<InputGroup error />);
175
- // Error icon is rendered inside
176
- expect(container.querySelector("input")).toBeInTheDocument();
177
- });
178
- });
179
-
180
- /* ------------------------------------------------------------------ */
181
- /* Field */
182
- /* ------------------------------------------------------------------ */
183
-
184
- describe("Field", () => {
185
- it("renders label and children", () => {
186
- render(
187
- <Field label="Email" htmlFor="email">
188
- <input id="email" type="email" />
189
- </Field>,
190
- );
191
- expect(screen.getByLabelText("Email")).toBeInTheDocument();
192
- });
193
-
194
- it("renders hint text", () => {
195
- render(
196
- <Field label="Username" hint="Must be unique">
197
- <input />
198
- </Field>,
199
- );
200
- expect(screen.getByText("Must be unique")).toBeInTheDocument();
201
- });
202
-
203
- it("renders error over hint when both provided", () => {
204
- render(
205
- <Field label="Field" hint="Hint" error="This is required">
206
- <input />
207
- </Field>,
208
- );
209
- expect(screen.getByText("This is required")).toBeInTheDocument();
210
- expect(screen.queryByText("Hint")).not.toBeInTheDocument();
211
- });
212
-
213
- it("renders required asterisk", () => {
214
- const { container } = render(
215
- <Field label="Name" required>
216
- <input />
217
- </Field>,
218
- );
219
- expect(container.querySelector("span")).toHaveTextContent("*");
220
- });
221
-
222
- it("renders cornerHint", () => {
223
- render(
224
- <Field label="Password" cornerHint="Optional">
225
- <input />
226
- </Field>,
227
- );
228
- expect(screen.getByText("Optional")).toBeInTheDocument();
229
- });
230
- });
231
-
232
- /* ------------------------------------------------------------------ */
233
- /* NativeSelect */
234
- /* ------------------------------------------------------------------ */
235
-
236
- describe("NativeSelect", () => {
237
- it("renders a combobox", () => {
238
- render(
239
- <NativeSelect>
240
- <NativeSelectOption value="a">Option A</NativeSelectOption>
241
- <NativeSelectOption value="b">Option B</NativeSelectOption>
242
- </NativeSelect>,
243
- );
244
- expect(screen.getByRole("combobox")).toBeInTheDocument();
245
- expect(screen.getByText("Option A")).toBeInTheDocument();
246
- expect(screen.getByText("Option B")).toBeInTheDocument();
247
- });
248
-
249
- it("is disabled when disabled prop is set", () => {
250
- render(
251
- <NativeSelect disabled>
252
- <NativeSelectOption value="x">X</NativeSelectOption>
253
- </NativeSelect>,
254
- );
255
- expect(screen.getByRole("combobox")).toBeDisabled();
256
- });
257
- });
258
-
259
- /* ------------------------------------------------------------------ */
260
- /* RadioCards */
261
- /* ------------------------------------------------------------------ */
262
-
263
- describe("RadioCards", () => {
264
- const options = [
265
- { value: "plan-a", label: "Basic" },
266
- { value: "plan-b", label: "Pro" },
267
- { value: "plan-c", label: "Enterprise" },
268
- ];
269
-
270
- it("renders all option labels", () => {
271
- render(<RadioCards options={options} />);
272
- expect(screen.getByText("Basic")).toBeInTheDocument();
273
- expect(screen.getByText("Pro")).toBeInTheDocument();
274
- expect(screen.getByText("Enterprise")).toBeInTheDocument();
275
- });
276
-
277
- it("marks the selected option as checked", () => {
278
- render(<RadioCards options={options} value="plan-b" />);
279
- const proBtn = screen.getByRole("radio", { name: "Pro" });
280
- expect(proBtn).toHaveAttribute("aria-checked", "true");
281
- });
282
-
283
- it("calls onValueChange when an option is clicked", () => {
284
- const onValueChange = vi.fn();
285
- render(<RadioCards options={options} onValueChange={onValueChange} />);
286
- fireEvent.click(screen.getByRole("radio", { name: "Enterprise" }));
287
- expect(onValueChange).toHaveBeenCalledWith("plan-c");
288
- });
289
-
290
- it("does not call onValueChange for disabled options", () => {
291
- const onValueChange = vi.fn();
292
- const opts = [{ value: "x", label: "Disabled", disabled: true }];
293
- render(<RadioCards options={opts} onValueChange={onValueChange} />);
294
- fireEvent.click(screen.getByRole("radio", { name: "Disabled" }));
295
- expect(onValueChange).not.toHaveBeenCalled();
296
- });
297
- });
@@ -1,388 +0,0 @@
1
- import { describe, it, expect, vi } from "vitest";
2
- import { render, screen, fireEvent } from "@testing-library/react";
3
- import {
4
- Breadcrumb,
5
- BreadcrumbList,
6
- BreadcrumbItem,
7
- BreadcrumbLink,
8
- BreadcrumbPage,
9
- BreadcrumbSeparator,
10
- BreadcrumbEllipsis,
11
- } from "../breadcrumb";
12
- import { SectionHeading } from "../section-heading";
13
- import { PageHeading } from "../page-heading";
14
- import { DescriptionList } from "../description-list";
15
- import { PropertyRow } from "../property-row";
16
- import { StackedList, StackedListItem } from "../stacked-list";
17
- import { GridList, GridListCard, GridListAccentCard } from "../grid-list";
18
- import { ActivityFeed } from "../activity-feed";
19
- import { CardHeading } from "../card-heading";
20
- import { ActionPanel } from "../action-panel";
21
- import { ButtonGroup, ButtonGroupItem } from "../button-group";
22
-
23
- /* ------------------------------------------------------------------ */
24
- /* Breadcrumb */
25
- /* ------------------------------------------------------------------ */
26
-
27
- describe("Breadcrumb", () => {
28
- it('renders nav with aria-label="breadcrumb"', () => {
29
- render(<Breadcrumb />);
30
- expect(screen.getByRole("navigation", { name: "breadcrumb" })).toBeInTheDocument();
31
- });
32
-
33
- it("renders full breadcrumb trail", () => {
34
- render(
35
- <Breadcrumb>
36
- <BreadcrumbList>
37
- <BreadcrumbItem>
38
- <BreadcrumbLink href="/">Home</BreadcrumbLink>
39
- </BreadcrumbItem>
40
- <BreadcrumbSeparator />
41
- <BreadcrumbItem>
42
- <BreadcrumbPage>Settings</BreadcrumbPage>
43
- </BreadcrumbItem>
44
- </BreadcrumbList>
45
- </Breadcrumb>,
46
- );
47
- expect(screen.getByRole("link", { name: "Home" })).toBeInTheDocument();
48
- expect(screen.getByText("Settings")).toBeInTheDocument();
49
- });
50
-
51
- it("renders BreadcrumbEllipsis", () => {
52
- const { container } = render(<BreadcrumbEllipsis />);
53
- expect(container.querySelector('[role="presentation"]')).toBeInTheDocument();
54
- });
55
- });
56
-
57
- /* ------------------------------------------------------------------ */
58
- /* SectionHeading */
59
- /* ------------------------------------------------------------------ */
60
-
61
- describe("SectionHeading", () => {
62
- it("renders title", () => {
63
- render(<SectionHeading title="Team Members" />);
64
- expect(screen.getByText("Team Members")).toBeInTheDocument();
65
- });
66
-
67
- it("renders description", () => {
68
- render(<SectionHeading title="Title" description="Manage your team." />);
69
- expect(screen.getByText("Manage your team.")).toBeInTheDocument();
70
- });
71
-
72
- it("renders actions", () => {
73
- render(<SectionHeading title="Title" actions={[{ label: "Add", onClick: () => {} }]} />);
74
- expect(screen.getByRole("button", { name: "Add" })).toBeInTheDocument();
75
- });
76
-
77
- it("renders action with href as link", () => {
78
- render(<SectionHeading title="Title" actions={[{ label: "View", href: "/all" }]} />);
79
- expect(screen.getByRole("link", { name: "View" })).toHaveAttribute("href", "/all");
80
- });
81
- });
82
-
83
- /* ------------------------------------------------------------------ */
84
- /* PageHeading */
85
- /* ------------------------------------------------------------------ */
86
-
87
- describe("PageHeading", () => {
88
- it("renders title as h1", () => {
89
- render(<PageHeading title="Dashboard" />);
90
- expect(screen.getByRole("heading", { name: "Dashboard", level: 1 })).toBeInTheDocument();
91
- });
92
-
93
- it("renders description", () => {
94
- render(<PageHeading title="Users" description="Manage all users." />);
95
- expect(screen.getByText("Manage all users.")).toBeInTheDocument();
96
- });
97
-
98
- it("renders breadcrumbs", () => {
99
- render(
100
- <PageHeading
101
- title="Configuration"
102
- breadcrumbs={[{ label: "Home", href: "/" }, { label: "Config" }]}
103
- />,
104
- );
105
- expect(screen.getByRole("link", { name: "Home" })).toBeInTheDocument();
106
- expect(screen.getByText("Config")).toBeInTheDocument();
107
- });
108
-
109
- it("renders badge as string", () => {
110
- render(<PageHeading title="Title" badge="Beta" />);
111
- expect(screen.getByText("Beta")).toBeInTheDocument();
112
- });
113
-
114
- it("renders badge as object", () => {
115
- render(<PageHeading title="Title" badge={{ label: "New", variant: "secondary" }} />);
116
- expect(screen.getByText("New")).toBeInTheDocument();
117
- });
118
-
119
- it("renders actions", () => {
120
- render(<PageHeading title="Title" actions={[{ label: "Export", primary: true }]} />);
121
- expect(screen.getByRole("button", { name: "Export" })).toBeInTheDocument();
122
- });
123
- });
124
-
125
- /* ------------------------------------------------------------------ */
126
- /* DescriptionList */
127
- /* ------------------------------------------------------------------ */
128
-
129
- describe("DescriptionList", () => {
130
- const items = [
131
- { term: "Name", details: "Alice" },
132
- { term: "Role", details: "Admin" },
133
- ];
134
-
135
- it("renders all terms and details", () => {
136
- render(<DescriptionList items={items} />);
137
- expect(screen.getByText("Name")).toBeInTheDocument();
138
- expect(screen.getByText("Alice")).toBeInTheDocument();
139
- expect(screen.getByText("Role")).toBeInTheDocument();
140
- expect(screen.getByText("Admin")).toBeInTheDocument();
141
- });
142
-
143
- it("renders title and description when provided", () => {
144
- render(<DescriptionList items={items} title="Details" description="User info" />);
145
- expect(screen.getByText("Details")).toBeInTheDocument();
146
- expect(screen.getByText("User info")).toBeInTheDocument();
147
- });
148
-
149
- it("renders 2-column layout", () => {
150
- render(<DescriptionList items={items} columns={2} />);
151
- expect(screen.getByText("Alice")).toBeInTheDocument();
152
- });
153
-
154
- it("renders action alongside detail", () => {
155
- render(
156
- <DescriptionList
157
- items={[{ term: "Email", details: "alice@test.com", action: <button>Edit</button> }]}
158
- />,
159
- );
160
- expect(screen.getByRole("button", { name: "Edit" })).toBeInTheDocument();
161
- });
162
- });
163
-
164
- /* ------------------------------------------------------------------ */
165
- /* PropertyRow */
166
- /* ------------------------------------------------------------------ */
167
-
168
- describe("PropertyRow", () => {
169
- it("renders label and children", () => {
170
- render(
171
- <PropertyRow label="Width">
172
- <input type="number" aria-label="width-input" />
173
- </PropertyRow>,
174
- );
175
- expect(screen.getByText("Width")).toBeInTheDocument();
176
- expect(screen.getByLabelText("width-input")).toBeInTheDocument();
177
- });
178
-
179
- it("renders without label", () => {
180
- render(
181
- <PropertyRow>
182
- <span>Controls</span>
183
- </PropertyRow>,
184
- );
185
- expect(screen.getByText("Controls")).toBeInTheDocument();
186
- });
187
- });
188
-
189
- /* ------------------------------------------------------------------ */
190
- /* StackedList */
191
- /* ------------------------------------------------------------------ */
192
-
193
- describe("StackedList", () => {
194
- it("renders as a list", () => {
195
- render(
196
- <StackedList>
197
- <StackedListItem>Item 1</StackedListItem>
198
- <StackedListItem>Item 2</StackedListItem>
199
- </StackedList>,
200
- );
201
- expect(screen.getByRole("list")).toBeInTheDocument();
202
- expect(screen.getByText("Item 1")).toBeInTheDocument();
203
- expect(screen.getByText("Item 2")).toBeInTheDocument();
204
- });
205
-
206
- it("renders item with href as link", () => {
207
- render(
208
- <StackedList>
209
- <StackedListItem href="/page">Go to page</StackedListItem>
210
- </StackedList>,
211
- );
212
- expect(screen.getByRole("link", { name: "Go to page" })).toBeInTheDocument();
213
- });
214
-
215
- it("renders item with onClick as button", () => {
216
- render(
217
- <StackedList>
218
- <StackedListItem onClick={() => {}}>Clickable</StackedListItem>
219
- </StackedList>,
220
- );
221
- expect(screen.getByRole("button", { name: "Clickable" })).toBeInTheDocument();
222
- });
223
- });
224
-
225
- /* ------------------------------------------------------------------ */
226
- /* GridList */
227
- /* ------------------------------------------------------------------ */
228
-
229
- describe("GridList", () => {
230
- it("renders as a list", () => {
231
- render(
232
- <GridList>
233
- <GridListCard title="Card A" />
234
- <GridListCard title="Card B" />
235
- </GridList>,
236
- );
237
- expect(screen.getByRole("list")).toBeInTheDocument();
238
- expect(screen.getByText("Card A")).toBeInTheDocument();
239
- expect(screen.getByText("Card B")).toBeInTheDocument();
240
- });
241
-
242
- it("GridListCard renders description", () => {
243
- render(
244
- <GridList>
245
- <GridListCard title="Item" description="Desc text" />
246
- </GridList>,
247
- );
248
- expect(screen.getByText("Desc text")).toBeInTheDocument();
249
- });
250
-
251
- it("GridListCard renders as link when href provided", () => {
252
- render(
253
- <GridList>
254
- <GridListCard title="Click" href="/target" />
255
- </GridList>,
256
- );
257
- expect(screen.getByRole("link")).toHaveAttribute("href", "/target");
258
- });
259
-
260
- it("GridListAccentCard renders title", () => {
261
- render(
262
- <GridList>
263
- <GridListAccentCard title="Project Alpha" initials="PA" />
264
- </GridList>,
265
- );
266
- expect(screen.getByText("Project Alpha")).toBeInTheDocument();
267
- });
268
-
269
- it("GridListAccentCard renders as link when href provided", () => {
270
- render(
271
- <GridList>
272
- <GridListAccentCard title="Project" href="/project" />
273
- </GridList>,
274
- );
275
- expect(screen.getByRole("link")).toHaveAttribute("href", "/project");
276
- });
277
- });
278
-
279
- /* ------------------------------------------------------------------ */
280
- /* ActivityFeed */
281
- /* ------------------------------------------------------------------ */
282
-
283
- describe("ActivityFeed", () => {
284
- it("renders all events", () => {
285
- render(
286
- <ActivityFeed
287
- events={[
288
- { id: 1, content: "User joined", date: "Today" },
289
- { id: 2, content: "File uploaded", date: "Yesterday" },
290
- ]}
291
- />,
292
- );
293
- expect(screen.getByText("User joined")).toBeInTheDocument();
294
- expect(screen.getByText("File uploaded")).toBeInTheDocument();
295
- });
296
-
297
- it("renders event dates", () => {
298
- render(<ActivityFeed events={[{ content: "Action", date: "Jan 1, 2026" }]} />);
299
- expect(screen.getByText("Jan 1, 2026")).toBeInTheDocument();
300
- });
301
- });
302
-
303
- /* ------------------------------------------------------------------ */
304
- /* CardHeading */
305
- /* ------------------------------------------------------------------ */
306
-
307
- describe("CardHeading", () => {
308
- it("renders title", () => {
309
- render(<CardHeading title="Team Settings" />);
310
- expect(screen.getByText("Team Settings")).toBeInTheDocument();
311
- });
312
-
313
- it("renders description", () => {
314
- render(<CardHeading title="Title" description="Subtitle here" />);
315
- expect(screen.getByText("Subtitle here")).toBeInTheDocument();
316
- });
317
-
318
- it("renders actions", () => {
319
- render(<CardHeading title="Title" actions={[{ label: "Save" }]} />);
320
- expect(screen.getByRole("button", { name: "Save" })).toBeInTheDocument();
321
- });
322
- });
323
-
324
- /* ------------------------------------------------------------------ */
325
- /* ActionPanel */
326
- /* ------------------------------------------------------------------ */
327
-
328
- describe("ActionPanel", () => {
329
- it("renders title", () => {
330
- render(<ActionPanel title="Danger Zone" />);
331
- expect(screen.getByText("Danger Zone")).toBeInTheDocument();
332
- });
333
-
334
- it("renders description", () => {
335
- render(<ActionPanel title="T" description="Delete this account." />);
336
- expect(screen.getByText("Delete this account.")).toBeInTheDocument();
337
- });
338
-
339
- it("renders action button", () => {
340
- render(<ActionPanel title="T" action={{ label: "Delete", variant: "destructive" }} />);
341
- expect(screen.getByRole("button", { name: "Delete" })).toBeInTheDocument();
342
- });
343
-
344
- it("renders inline layout without error", () => {
345
- render(<ActionPanel title="Info" layout="inline" action={{ label: "Go" }} />);
346
- expect(screen.getByText("Info")).toBeInTheDocument();
347
- });
348
-
349
- it("renders muted variant without error", () => {
350
- render(<ActionPanel title="Note" variant="muted" />);
351
- expect(screen.getByText("Note")).toBeInTheDocument();
352
- });
353
-
354
- it("renders link", () => {
355
- render(<ActionPanel title="T" link={{ label: "Learn more", href: "/docs" }} />);
356
- expect(screen.getByText("Learn more")).toBeInTheDocument();
357
- });
358
- });
359
-
360
- /* ------------------------------------------------------------------ */
361
- /* ButtonGroup */
362
- /* ------------------------------------------------------------------ */
363
-
364
- describe("ButtonGroup", () => {
365
- it("renders items", () => {
366
- render(
367
- <ButtonGroup>
368
- <ButtonGroupItem>Day</ButtonGroupItem>
369
- <ButtonGroupItem>Week</ButtonGroupItem>
370
- <ButtonGroupItem active>Month</ButtonGroupItem>
371
- </ButtonGroup>,
372
- );
373
- expect(screen.getByRole("button", { name: "Day" })).toBeInTheDocument();
374
- expect(screen.getByRole("button", { name: "Week" })).toBeInTheDocument();
375
- expect(screen.getByRole("button", { name: "Month" })).toBeInTheDocument();
376
- });
377
-
378
- it("calls onClick on item click", () => {
379
- const onClick = vi.fn();
380
- render(
381
- <ButtonGroup>
382
- <ButtonGroupItem onClick={onClick}>Click me</ButtonGroupItem>
383
- </ButtonGroup>,
384
- );
385
- fireEvent.click(screen.getByRole("button", { name: "Click me" }));
386
- expect(onClick).toHaveBeenCalledTimes(1);
387
- });
388
- });