@hilum/ui 0.1.1 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,138 +0,0 @@
1
- import { describe, it, expect } from "vitest";
2
- import { render, screen } from "@testing-library/react";
3
- import { Alert, AlertTitle, AlertDescription } from "../alert";
4
- import { Progress } from "../progress";
5
- import { Steps } from "../steps";
6
- import { EmptyState } from "../empty-state";
7
- import type { Step } from "../steps";
8
-
9
- /* ------------------------------------------------------------------ */
10
- /* Alert */
11
- /* ------------------------------------------------------------------ */
12
-
13
- describe("Alert", () => {
14
- it('renders with role="alert"', () => {
15
- render(<Alert>Message</Alert>);
16
- expect(screen.getByRole("alert")).toBeInTheDocument();
17
- });
18
-
19
- it("renders sub-components together", () => {
20
- render(
21
- <Alert>
22
- <AlertTitle>Warning</AlertTitle>
23
- <AlertDescription>Something went wrong.</AlertDescription>
24
- </Alert>,
25
- );
26
- expect(screen.getByText("Warning")).toBeInTheDocument();
27
- expect(screen.getByText("Something went wrong.")).toBeInTheDocument();
28
- });
29
-
30
- it("renders all variants without error", () => {
31
- const variants = ["default", "info", "success", "warning", "destructive"] as const;
32
- for (const variant of variants) {
33
- const { unmount } = render(<Alert variant={variant}>Alert</Alert>);
34
- expect(screen.getByRole("alert")).toBeInTheDocument();
35
- unmount();
36
- }
37
- });
38
- });
39
-
40
- /* ------------------------------------------------------------------ */
41
- /* Progress */
42
- /* ------------------------------------------------------------------ */
43
-
44
- describe("Progress", () => {
45
- it("renders with a progressbar role", () => {
46
- render(<Progress value={50} />);
47
- expect(screen.getByRole("progressbar")).toBeInTheDocument();
48
- });
49
-
50
- it("reflects the correct value", () => {
51
- render(<Progress value={75} />);
52
- expect(screen.getByRole("progressbar")).toHaveAttribute("aria-valuenow", "75");
53
- });
54
-
55
- it("renders at 0%", () => {
56
- render(<Progress value={0} />);
57
- expect(screen.getByRole("progressbar")).toHaveAttribute("aria-valuenow", "0");
58
- });
59
-
60
- it("renders at 100%", () => {
61
- render(<Progress value={100} />);
62
- expect(screen.getByRole("progressbar")).toHaveAttribute("aria-valuenow", "100");
63
- });
64
- });
65
-
66
- /* ------------------------------------------------------------------ */
67
- /* Steps */
68
- /* ------------------------------------------------------------------ */
69
-
70
- const STEPS: Step[] = [
71
- { name: "Cart", status: "complete" },
72
- { name: "Shipping", status: "current" },
73
- { name: "Payment", status: "upcoming" },
74
- ];
75
-
76
- describe("Steps", () => {
77
- it("renders all step names in circles variant (default)", () => {
78
- render(<Steps steps={STEPS} />);
79
- expect(screen.getByText("Cart")).toBeInTheDocument();
80
- expect(screen.getByText("Shipping")).toBeInTheDocument();
81
- expect(screen.getByText("Payment")).toBeInTheDocument();
82
- });
83
-
84
- it("renders bullets variant without error", () => {
85
- render(<Steps steps={STEPS} variant="bullets" />);
86
- expect(screen.getByRole("navigation", { name: /progress/i })).toBeInTheDocument();
87
- });
88
-
89
- it("renders progress variant without error", () => {
90
- render(<Steps steps={STEPS} variant="progress" />);
91
- expect(screen.getByText("Cart")).toBeInTheDocument();
92
- });
93
-
94
- it("bullets variant shows step count", () => {
95
- render(<Steps steps={STEPS} variant="bullets" />);
96
- expect(screen.getByText(/Step 2 of 3/i)).toBeInTheDocument();
97
- });
98
-
99
- it("renders with descriptions in circles variant", () => {
100
- const stepsWithDesc: Step[] = [
101
- { name: "Step 1", description: "First step", status: "complete" },
102
- { name: "Step 2", status: "current" },
103
- ];
104
- render(<Steps steps={stepsWithDesc} />);
105
- expect(screen.getByText("First step")).toBeInTheDocument();
106
- });
107
- });
108
-
109
- /* ------------------------------------------------------------------ */
110
- /* EmptyState */
111
- /* ------------------------------------------------------------------ */
112
-
113
- describe("EmptyState", () => {
114
- it("renders the title", () => {
115
- render(<EmptyState title="No results found" />);
116
- expect(screen.getByText("No results found")).toBeInTheDocument();
117
- });
118
-
119
- it("renders description when provided", () => {
120
- render(<EmptyState title="Empty" description="Try a different search." />);
121
- expect(screen.getByText("Try a different search.")).toBeInTheDocument();
122
- });
123
-
124
- it("renders action button with label", () => {
125
- render(<EmptyState title="Empty" action={{ label: "Add item" }} />);
126
- expect(screen.getByRole("button", { name: "Add item" })).toBeInTheDocument();
127
- });
128
-
129
- it("renders action as anchor when href is provided", () => {
130
- render(<EmptyState title="Empty" action={{ label: "Create", href: "/new" }} />);
131
- expect(screen.getByRole("link", { name: "Create" })).toHaveAttribute("href", "/new");
132
- });
133
-
134
- it("renders icon when provided", () => {
135
- render(<EmptyState title="Empty" icon={<span data-testid="icon">📭</span>} />);
136
- expect(screen.getByTestId("icon")).toBeInTheDocument();
137
- });
138
- });
@@ -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
- });