@fpkit/acss 3.7.0 → 3.8.0
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/libs/components/checkbox/checkbox.css +1 -0
- package/libs/components/checkbox/checkbox.css.map +1 -0
- package/libs/components/checkbox/checkbox.min.css +3 -0
- package/libs/index.cjs +27 -26
- package/libs/index.cjs.map +1 -1
- package/libs/index.css +1 -1
- package/libs/index.css.map +1 -1
- package/libs/index.d.cts +356 -1
- package/libs/index.d.ts +356 -1
- package/libs/index.js +5 -5
- package/libs/index.js.map +1 -1
- package/package.json +2 -2
- package/src/components/checkbox/README.mdx +263 -0
- package/src/components/checkbox/STYLES.mdx +434 -0
- package/src/components/checkbox/checkbox.scss +432 -0
- package/src/components/checkbox/checkbox.stories.tsx +607 -0
- package/src/components/checkbox/checkbox.test.tsx +535 -0
- package/src/components/checkbox/checkbox.tsx +575 -0
- package/src/components/form/README.mdx +173 -146
- package/src/index.scss +1 -0
- package/src/index.ts +7 -0
- package/src/sass/_columns.scss +13 -9
- package/src/styles/checkbox/checkbox.css +372 -0
- package/src/styles/checkbox/checkbox.css.map +1 -0
- package/src/styles/index.css +371 -0
- package/src/styles/index.css.map +1 -1
|
@@ -0,0 +1,535 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { describe, it, expect, vi } from "vitest";
|
|
3
|
+
import { render, screen } from "@testing-library/react";
|
|
4
|
+
import userEvent from "@testing-library/user-event";
|
|
5
|
+
import { Checkbox } from "./checkbox";
|
|
6
|
+
|
|
7
|
+
describe("Checkbox", () => {
|
|
8
|
+
// ==========================================================================
|
|
9
|
+
// Category 1: Rendering (8 tests)
|
|
10
|
+
// ==========================================================================
|
|
11
|
+
|
|
12
|
+
describe("Rendering", () => {
|
|
13
|
+
it("renders with label prop", () => {
|
|
14
|
+
render(<Checkbox id="test" label="Accept terms" />);
|
|
15
|
+
|
|
16
|
+
const checkbox = screen.getByRole("checkbox");
|
|
17
|
+
const label = screen.getByText("Accept terms");
|
|
18
|
+
|
|
19
|
+
expect(checkbox).toBeInTheDocument();
|
|
20
|
+
expect(label).toBeInTheDocument();
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it("renders with children prop", () => {
|
|
24
|
+
render(<Checkbox id="test">Subscribe to newsletter</Checkbox>);
|
|
25
|
+
|
|
26
|
+
const checkbox = screen.getByRole("checkbox");
|
|
27
|
+
const label = screen.getByText("Subscribe to newsletter");
|
|
28
|
+
|
|
29
|
+
expect(checkbox).toBeInTheDocument();
|
|
30
|
+
expect(label).toBeInTheDocument();
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it("renders with description", () => {
|
|
34
|
+
render(
|
|
35
|
+
<Checkbox
|
|
36
|
+
id="test"
|
|
37
|
+
label="Enable notifications"
|
|
38
|
+
description="Receive email updates"
|
|
39
|
+
/>
|
|
40
|
+
);
|
|
41
|
+
|
|
42
|
+
const description = screen.getByText("Receive email updates");
|
|
43
|
+
expect(description).toBeInTheDocument();
|
|
44
|
+
expect(description).toHaveAttribute("id", "test-description");
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it("renders with error message when invalid", () => {
|
|
48
|
+
render(
|
|
49
|
+
<Checkbox
|
|
50
|
+
id="test"
|
|
51
|
+
label="Required field"
|
|
52
|
+
validationState="invalid"
|
|
53
|
+
errorMessage="This field is required"
|
|
54
|
+
/>
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
const error = screen.getByText("This field is required");
|
|
58
|
+
expect(error).toBeInTheDocument();
|
|
59
|
+
expect(error).toHaveAttribute("id", "test-error");
|
|
60
|
+
expect(error).toHaveAttribute("role", "alert");
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it("renders size variants correctly", () => {
|
|
64
|
+
const { rerender } = render(
|
|
65
|
+
<Checkbox id="test" label="Small" size="sm" />
|
|
66
|
+
);
|
|
67
|
+
expect(
|
|
68
|
+
screen.getByRole("checkbox").closest("[data-checkbox]")
|
|
69
|
+
).toHaveAttribute("data-checkbox", "sm primary");
|
|
70
|
+
|
|
71
|
+
rerender(<Checkbox id="test" label="Medium" size="md" />);
|
|
72
|
+
expect(
|
|
73
|
+
screen.getByRole("checkbox").closest("[data-checkbox]")
|
|
74
|
+
).toHaveAttribute("data-checkbox", "md primary");
|
|
75
|
+
|
|
76
|
+
rerender(<Checkbox id="test" label="Large" size="lg" />);
|
|
77
|
+
expect(
|
|
78
|
+
screen.getByRole("checkbox").closest("[data-checkbox]")
|
|
79
|
+
).toHaveAttribute("data-checkbox", "lg primary");
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
it("renders color variants correctly", () => {
|
|
83
|
+
const { rerender } = render(
|
|
84
|
+
<Checkbox id="test" label="Primary" color="primary" />
|
|
85
|
+
);
|
|
86
|
+
expect(
|
|
87
|
+
screen.getByRole("checkbox").closest("[data-checkbox]")
|
|
88
|
+
).toHaveAttribute("data-checkbox", "md primary");
|
|
89
|
+
|
|
90
|
+
rerender(<Checkbox id="test" label="Secondary" color="secondary" />);
|
|
91
|
+
expect(
|
|
92
|
+
screen.getByRole("checkbox").closest("[data-checkbox]")
|
|
93
|
+
).toHaveAttribute("data-checkbox", "md secondary");
|
|
94
|
+
|
|
95
|
+
rerender(<Checkbox id="test" label="Error" color="error" />);
|
|
96
|
+
expect(
|
|
97
|
+
screen.getByRole("checkbox").closest("[data-checkbox]")
|
|
98
|
+
).toHaveAttribute("data-checkbox", "md error");
|
|
99
|
+
|
|
100
|
+
rerender(<Checkbox id="test" label="Success" color="success" />);
|
|
101
|
+
expect(
|
|
102
|
+
screen.getByRole("checkbox").closest("[data-checkbox]")
|
|
103
|
+
).toHaveAttribute("data-checkbox", "md success");
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
it("renders with custom classes and styles", () => {
|
|
107
|
+
render(
|
|
108
|
+
<Checkbox
|
|
109
|
+
id="test"
|
|
110
|
+
label="Custom"
|
|
111
|
+
classes="custom-class"
|
|
112
|
+
styles={{ "--checkbox-size": "2rem" } as React.CSSProperties}
|
|
113
|
+
/>
|
|
114
|
+
);
|
|
115
|
+
|
|
116
|
+
const wrapper = screen.getByRole("checkbox").closest("[data-checkbox]");
|
|
117
|
+
expect(wrapper).toHaveClass("checkbox-wrapper");
|
|
118
|
+
expect(wrapper).toHaveClass("custom-class");
|
|
119
|
+
expect(wrapper).toHaveStyle({ "--checkbox-size": "2rem" });
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
it("renders required indicator when required", () => {
|
|
123
|
+
render(<Checkbox id="test" label="Required field" required />);
|
|
124
|
+
|
|
125
|
+
const requiredIndicator = screen.getByLabelText("required");
|
|
126
|
+
expect(requiredIndicator).toBeInTheDocument();
|
|
127
|
+
expect(requiredIndicator).toHaveTextContent("*");
|
|
128
|
+
});
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
// ==========================================================================
|
|
132
|
+
// Category 2: Interaction (4 tests)
|
|
133
|
+
// ==========================================================================
|
|
134
|
+
|
|
135
|
+
describe("Interaction", () => {
|
|
136
|
+
it("toggles on click", async () => {
|
|
137
|
+
const user = userEvent.setup();
|
|
138
|
+
render(<Checkbox id="test" label="Click me" />);
|
|
139
|
+
|
|
140
|
+
const checkbox = screen.getByRole("checkbox") as HTMLInputElement;
|
|
141
|
+
expect(checkbox.checked).toBe(false);
|
|
142
|
+
|
|
143
|
+
await user.click(checkbox);
|
|
144
|
+
expect(checkbox.checked).toBe(true);
|
|
145
|
+
|
|
146
|
+
await user.click(checkbox);
|
|
147
|
+
expect(checkbox.checked).toBe(false);
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
it("toggles on Space key press", async () => {
|
|
151
|
+
const user = userEvent.setup();
|
|
152
|
+
render(<Checkbox id="test" label="Press space" />);
|
|
153
|
+
|
|
154
|
+
const checkbox = screen.getByRole("checkbox") as HTMLInputElement;
|
|
155
|
+
|
|
156
|
+
// Focus the checkbox first
|
|
157
|
+
checkbox.focus();
|
|
158
|
+
expect(checkbox).toHaveFocus();
|
|
159
|
+
|
|
160
|
+
await user.keyboard(" ");
|
|
161
|
+
expect(checkbox.checked).toBe(true);
|
|
162
|
+
|
|
163
|
+
await user.keyboard(" ");
|
|
164
|
+
expect(checkbox.checked).toBe(false);
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
it("fires onChange handler", async () => {
|
|
168
|
+
const user = userEvent.setup();
|
|
169
|
+
const handleChange = vi.fn();
|
|
170
|
+
render(<Checkbox id="test" label="Change me" onChange={handleChange} />);
|
|
171
|
+
|
|
172
|
+
const checkbox = screen.getByRole("checkbox");
|
|
173
|
+
|
|
174
|
+
await user.click(checkbox);
|
|
175
|
+
expect(handleChange).toHaveBeenCalledTimes(1);
|
|
176
|
+
expect(handleChange).toHaveBeenCalledWith(
|
|
177
|
+
expect.objectContaining({
|
|
178
|
+
target: expect.objectContaining({ checked: true }),
|
|
179
|
+
})
|
|
180
|
+
);
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
it("prevents interaction when disabled", async () => {
|
|
184
|
+
const user = userEvent.setup();
|
|
185
|
+
const handleChange = vi.fn();
|
|
186
|
+
render(
|
|
187
|
+
<Checkbox
|
|
188
|
+
id="test"
|
|
189
|
+
label="Disabled"
|
|
190
|
+
disabled
|
|
191
|
+
onChange={handleChange}
|
|
192
|
+
/>
|
|
193
|
+
);
|
|
194
|
+
|
|
195
|
+
const checkbox = screen.getByRole("checkbox");
|
|
196
|
+
|
|
197
|
+
await user.click(checkbox);
|
|
198
|
+
expect(handleChange).not.toHaveBeenCalled();
|
|
199
|
+
});
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
// ==========================================================================
|
|
203
|
+
// Category 3: Controlled/Uncontrolled (3 tests)
|
|
204
|
+
// ==========================================================================
|
|
205
|
+
|
|
206
|
+
describe("Controlled and Uncontrolled modes", () => {
|
|
207
|
+
it("works in controlled mode with checked prop", async () => {
|
|
208
|
+
const user = userEvent.setup();
|
|
209
|
+
const TestComponent = () => {
|
|
210
|
+
const [checked, setChecked] = React.useState(false);
|
|
211
|
+
return (
|
|
212
|
+
<Checkbox
|
|
213
|
+
id="test"
|
|
214
|
+
label="Controlled"
|
|
215
|
+
checked={checked}
|
|
216
|
+
onChange={(e) => setChecked(e.target.checked)}
|
|
217
|
+
/>
|
|
218
|
+
);
|
|
219
|
+
};
|
|
220
|
+
|
|
221
|
+
render(<TestComponent />);
|
|
222
|
+
const checkbox = screen.getByRole("checkbox") as HTMLInputElement;
|
|
223
|
+
|
|
224
|
+
expect(checkbox.checked).toBe(false);
|
|
225
|
+
|
|
226
|
+
await user.click(checkbox);
|
|
227
|
+
expect(checkbox.checked).toBe(true);
|
|
228
|
+
|
|
229
|
+
await user.click(checkbox);
|
|
230
|
+
expect(checkbox.checked).toBe(false);
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
it("works in uncontrolled mode with defaultChecked prop", async () => {
|
|
234
|
+
const user = userEvent.setup();
|
|
235
|
+
render(
|
|
236
|
+
<Checkbox id="test" label="Uncontrolled" defaultChecked={true} />
|
|
237
|
+
);
|
|
238
|
+
|
|
239
|
+
const checkbox = screen.getByRole("checkbox") as HTMLInputElement;
|
|
240
|
+
expect(checkbox.checked).toBe(true);
|
|
241
|
+
|
|
242
|
+
await user.click(checkbox);
|
|
243
|
+
expect(checkbox.checked).toBe(false);
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
it("uses checked prop when both checked and defaultChecked are provided", () => {
|
|
247
|
+
render(
|
|
248
|
+
<Checkbox
|
|
249
|
+
id="test"
|
|
250
|
+
label="Both props"
|
|
251
|
+
checked={true}
|
|
252
|
+
defaultChecked={false}
|
|
253
|
+
onChange={vi.fn()}
|
|
254
|
+
/>
|
|
255
|
+
);
|
|
256
|
+
|
|
257
|
+
const checkbox = screen.getByRole("checkbox") as HTMLInputElement;
|
|
258
|
+
expect(checkbox.checked).toBe(true);
|
|
259
|
+
});
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
// ==========================================================================
|
|
263
|
+
// Category 4: Indeterminate State (2 tests)
|
|
264
|
+
// ==========================================================================
|
|
265
|
+
|
|
266
|
+
describe("Indeterminate state", () => {
|
|
267
|
+
it("applies indeterminate property to input", () => {
|
|
268
|
+
render(<Checkbox id="test" label="Indeterminate" indeterminate={true} />);
|
|
269
|
+
|
|
270
|
+
const checkbox = screen.getByRole("checkbox") as HTMLInputElement;
|
|
271
|
+
expect(checkbox.indeterminate).toBe(true);
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
it("updates indeterminate property when prop changes", () => {
|
|
275
|
+
const { rerender } = render(
|
|
276
|
+
<Checkbox id="test" label="Toggle indeterminate" indeterminate={false} />
|
|
277
|
+
);
|
|
278
|
+
|
|
279
|
+
const checkbox = screen.getByRole("checkbox") as HTMLInputElement;
|
|
280
|
+
expect(checkbox.indeterminate).toBe(false);
|
|
281
|
+
|
|
282
|
+
rerender(
|
|
283
|
+
<Checkbox id="test" label="Toggle indeterminate" indeterminate={true} />
|
|
284
|
+
);
|
|
285
|
+
expect(checkbox.indeterminate).toBe(true);
|
|
286
|
+
});
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
// ==========================================================================
|
|
290
|
+
// Category 5: Disabled State (4 tests)
|
|
291
|
+
// ==========================================================================
|
|
292
|
+
|
|
293
|
+
describe("Disabled state", () => {
|
|
294
|
+
it("sets aria-disabled attribute", () => {
|
|
295
|
+
render(<Checkbox id="test" label="Disabled" disabled />);
|
|
296
|
+
|
|
297
|
+
const checkbox = screen.getByRole("checkbox");
|
|
298
|
+
expect(checkbox).toHaveAttribute("aria-disabled", "true");
|
|
299
|
+
});
|
|
300
|
+
|
|
301
|
+
it("remains focusable for accessibility", async () => {
|
|
302
|
+
const user = userEvent.setup();
|
|
303
|
+
render(<Checkbox id="test" label="Disabled focusable" disabled />);
|
|
304
|
+
|
|
305
|
+
const checkbox = screen.getByRole("checkbox");
|
|
306
|
+
|
|
307
|
+
await user.tab();
|
|
308
|
+
expect(checkbox).toHaveFocus();
|
|
309
|
+
});
|
|
310
|
+
|
|
311
|
+
it("prevents onChange when disabled", async () => {
|
|
312
|
+
const user = userEvent.setup();
|
|
313
|
+
const handleChange = vi.fn();
|
|
314
|
+
render(
|
|
315
|
+
<Checkbox
|
|
316
|
+
id="test"
|
|
317
|
+
label="Disabled no change"
|
|
318
|
+
disabled
|
|
319
|
+
onChange={handleChange}
|
|
320
|
+
/>
|
|
321
|
+
);
|
|
322
|
+
|
|
323
|
+
const checkbox = screen.getByRole("checkbox");
|
|
324
|
+
|
|
325
|
+
await user.click(checkbox);
|
|
326
|
+
expect(handleChange).not.toHaveBeenCalled();
|
|
327
|
+
});
|
|
328
|
+
|
|
329
|
+
it("applies .is-disabled class", () => {
|
|
330
|
+
render(<Checkbox id="test" label="Disabled class" disabled />);
|
|
331
|
+
|
|
332
|
+
const wrapper = screen.getByRole("checkbox").closest("[data-checkbox]");
|
|
333
|
+
expect(wrapper).toHaveClass("is-disabled");
|
|
334
|
+
});
|
|
335
|
+
});
|
|
336
|
+
|
|
337
|
+
// ==========================================================================
|
|
338
|
+
// Category 6: Validation (3 tests)
|
|
339
|
+
// ==========================================================================
|
|
340
|
+
|
|
341
|
+
describe("Validation", () => {
|
|
342
|
+
it("sets aria-invalid when validationState is invalid", () => {
|
|
343
|
+
render(
|
|
344
|
+
<Checkbox
|
|
345
|
+
id="test"
|
|
346
|
+
label="Invalid"
|
|
347
|
+
validationState="invalid"
|
|
348
|
+
errorMessage="Error"
|
|
349
|
+
/>
|
|
350
|
+
);
|
|
351
|
+
|
|
352
|
+
const checkbox = screen.getByRole("checkbox");
|
|
353
|
+
expect(checkbox).toHaveAttribute("aria-invalid", "true");
|
|
354
|
+
});
|
|
355
|
+
|
|
356
|
+
it("links error message with aria-describedby", () => {
|
|
357
|
+
render(
|
|
358
|
+
<Checkbox
|
|
359
|
+
id="test"
|
|
360
|
+
label="With error"
|
|
361
|
+
validationState="invalid"
|
|
362
|
+
errorMessage="This field is required"
|
|
363
|
+
/>
|
|
364
|
+
);
|
|
365
|
+
|
|
366
|
+
const checkbox = screen.getByRole("checkbox");
|
|
367
|
+
expect(checkbox).toHaveAttribute("aria-describedby", "test-error");
|
|
368
|
+
});
|
|
369
|
+
|
|
370
|
+
it("displays error message when invalid", () => {
|
|
371
|
+
render(
|
|
372
|
+
<Checkbox
|
|
373
|
+
id="test"
|
|
374
|
+
label="Show error"
|
|
375
|
+
validationState="invalid"
|
|
376
|
+
errorMessage="Please check this box"
|
|
377
|
+
/>
|
|
378
|
+
);
|
|
379
|
+
|
|
380
|
+
const error = screen.getByText("Please check this box");
|
|
381
|
+
expect(error).toBeInTheDocument();
|
|
382
|
+
expect(error).toHaveAttribute("role", "alert");
|
|
383
|
+
});
|
|
384
|
+
});
|
|
385
|
+
|
|
386
|
+
// ==========================================================================
|
|
387
|
+
// Category 7: Accessibility (5 tests)
|
|
388
|
+
// ==========================================================================
|
|
389
|
+
|
|
390
|
+
describe("Accessibility", () => {
|
|
391
|
+
it("has accessible name from label", () => {
|
|
392
|
+
render(<Checkbox id="test" label="Accessible checkbox" />);
|
|
393
|
+
|
|
394
|
+
const checkbox = screen.getByRole("checkbox", {
|
|
395
|
+
name: "Accessible checkbox",
|
|
396
|
+
});
|
|
397
|
+
expect(checkbox).toBeInTheDocument();
|
|
398
|
+
});
|
|
399
|
+
|
|
400
|
+
it("links description with aria-describedby", () => {
|
|
401
|
+
render(
|
|
402
|
+
<Checkbox
|
|
403
|
+
id="test"
|
|
404
|
+
label="With description"
|
|
405
|
+
description="Additional context"
|
|
406
|
+
/>
|
|
407
|
+
);
|
|
408
|
+
|
|
409
|
+
const checkbox = screen.getByRole("checkbox");
|
|
410
|
+
expect(checkbox).toHaveAttribute("aria-describedby", "test-description");
|
|
411
|
+
|
|
412
|
+
const description = screen.getByText("Additional context");
|
|
413
|
+
expect(description).toHaveAttribute("id", "test-description");
|
|
414
|
+
});
|
|
415
|
+
|
|
416
|
+
it("links both description and error with aria-describedby", () => {
|
|
417
|
+
render(
|
|
418
|
+
<Checkbox
|
|
419
|
+
id="test"
|
|
420
|
+
label="With both"
|
|
421
|
+
description="Description text"
|
|
422
|
+
validationState="invalid"
|
|
423
|
+
errorMessage="Error text"
|
|
424
|
+
/>
|
|
425
|
+
);
|
|
426
|
+
|
|
427
|
+
const checkbox = screen.getByRole("checkbox");
|
|
428
|
+
expect(checkbox).toHaveAttribute(
|
|
429
|
+
"aria-describedby",
|
|
430
|
+
"test-description test-error"
|
|
431
|
+
);
|
|
432
|
+
});
|
|
433
|
+
|
|
434
|
+
it("sets aria-required when required", () => {
|
|
435
|
+
render(<Checkbox id="test" label="Required checkbox" required />);
|
|
436
|
+
|
|
437
|
+
const checkbox = screen.getByRole("checkbox");
|
|
438
|
+
expect(checkbox).toHaveAttribute("aria-required", "true");
|
|
439
|
+
});
|
|
440
|
+
|
|
441
|
+
it("maintains keyboard navigation", async () => {
|
|
442
|
+
const user = userEvent.setup();
|
|
443
|
+
render(
|
|
444
|
+
<>
|
|
445
|
+
<Checkbox id="first" label="First" />
|
|
446
|
+
<Checkbox id="second" label="Second" />
|
|
447
|
+
<Checkbox id="third" label="Third" />
|
|
448
|
+
</>
|
|
449
|
+
);
|
|
450
|
+
|
|
451
|
+
const first = screen.getByLabelText("First");
|
|
452
|
+
const second = screen.getByLabelText("Second");
|
|
453
|
+
const third = screen.getByLabelText("Third");
|
|
454
|
+
|
|
455
|
+
await user.tab();
|
|
456
|
+
expect(first).toHaveFocus();
|
|
457
|
+
|
|
458
|
+
await user.tab();
|
|
459
|
+
expect(second).toHaveFocus();
|
|
460
|
+
|
|
461
|
+
await user.tab();
|
|
462
|
+
expect(third).toHaveFocus();
|
|
463
|
+
});
|
|
464
|
+
});
|
|
465
|
+
|
|
466
|
+
// ==========================================================================
|
|
467
|
+
// Category 8: Label Position (2 tests)
|
|
468
|
+
// ==========================================================================
|
|
469
|
+
|
|
470
|
+
describe("Label position", () => {
|
|
471
|
+
it("positions label on the right by default", () => {
|
|
472
|
+
render(<Checkbox id="test" label="Label on right" />);
|
|
473
|
+
|
|
474
|
+
const container = screen
|
|
475
|
+
.getByRole("checkbox")
|
|
476
|
+
.closest(".checkbox-container");
|
|
477
|
+
const label = screen.getByText("Label on right");
|
|
478
|
+
|
|
479
|
+
// Check that label comes after the input wrapper in DOM order
|
|
480
|
+
expect(container?.children[1]).toContain(label);
|
|
481
|
+
});
|
|
482
|
+
|
|
483
|
+
it("positions label on the left when labelPosition='left'", () => {
|
|
484
|
+
render(
|
|
485
|
+
<Checkbox id="test" label="Label on left" labelPosition="left" />
|
|
486
|
+
);
|
|
487
|
+
|
|
488
|
+
const container = screen
|
|
489
|
+
.getByRole("checkbox")
|
|
490
|
+
.closest(".checkbox-container");
|
|
491
|
+
const label = screen.getByText("Label on left");
|
|
492
|
+
|
|
493
|
+
// Check that label comes before the input wrapper in DOM order
|
|
494
|
+
expect(container?.children[0]).toContain(label);
|
|
495
|
+
});
|
|
496
|
+
});
|
|
497
|
+
|
|
498
|
+
// ==========================================================================
|
|
499
|
+
// Additional Edge Cases
|
|
500
|
+
// ==========================================================================
|
|
501
|
+
|
|
502
|
+
describe("Edge cases", () => {
|
|
503
|
+
it("handles missing label gracefully", () => {
|
|
504
|
+
render(<Checkbox id="test" />);
|
|
505
|
+
|
|
506
|
+
const checkbox = screen.getByRole("checkbox");
|
|
507
|
+
expect(checkbox).toBeInTheDocument();
|
|
508
|
+
});
|
|
509
|
+
|
|
510
|
+
it("forwards ref correctly", () => {
|
|
511
|
+
const ref = React.createRef<HTMLInputElement>();
|
|
512
|
+
render(<Checkbox id="test" label="With ref" ref={ref} />);
|
|
513
|
+
|
|
514
|
+
expect(ref.current).toBeInstanceOf(HTMLInputElement);
|
|
515
|
+
expect(ref.current?.type).toBe("checkbox");
|
|
516
|
+
});
|
|
517
|
+
|
|
518
|
+
it("passes through additional input props", () => {
|
|
519
|
+
render(
|
|
520
|
+
<Checkbox
|
|
521
|
+
id="test"
|
|
522
|
+
label="With props"
|
|
523
|
+
name="agreement"
|
|
524
|
+
value="accepted"
|
|
525
|
+
data-testid="custom-checkbox"
|
|
526
|
+
/>
|
|
527
|
+
);
|
|
528
|
+
|
|
529
|
+
const checkbox = screen.getByRole("checkbox");
|
|
530
|
+
expect(checkbox).toHaveAttribute("name", "agreement");
|
|
531
|
+
expect(checkbox).toHaveAttribute("value", "accepted");
|
|
532
|
+
expect(checkbox).toHaveAttribute("data-testid", "custom-checkbox");
|
|
533
|
+
});
|
|
534
|
+
});
|
|
535
|
+
});
|