@fpkit/acss 0.5.13 → 0.6.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/{chunk-PQ2K3BM6.cjs → chunk-2NRIP6RB.cjs} +3 -3
- package/libs/chunk-33PNJ4LO.cjs +15 -0
- package/libs/chunk-33PNJ4LO.cjs.map +1 -0
- package/libs/chunk-4BZKFPEC.cjs +17 -0
- package/libs/chunk-4BZKFPEC.cjs.map +1 -0
- package/libs/{chunk-772NRB75.js → chunk-5QD3DWFI.js} +2 -2
- package/libs/chunk-6SAHIYCZ.js +7 -0
- package/libs/chunk-6SAHIYCZ.js.map +1 -0
- package/libs/{chunk-3MKLDCKQ.cjs → chunk-6WTC4JXH.cjs} +3 -3
- package/libs/chunk-75QHTLFO.js +7 -0
- package/libs/chunk-75QHTLFO.js.map +1 -0
- package/libs/{chunk-ZANSFMTD.js → chunk-7XPFW7CB.js} +3 -3
- package/libs/chunk-BFK62VX5.js +5 -0
- package/libs/chunk-BFK62VX5.js.map +1 -0
- package/libs/{chunk-ROZI23GS.cjs → chunk-DKTHCQ5P.cjs} +4 -4
- package/libs/chunk-E2AJURUW.cjs +13 -0
- package/libs/chunk-E2AJURUW.cjs.map +1 -0
- package/libs/{chunk-L75OQKEI.cjs → chunk-ENTCUJ3A.cjs} +3 -3
- package/libs/chunk-ENTCUJ3A.cjs.map +1 -0
- package/libs/chunk-F5EYMVQM.js +10 -0
- package/libs/chunk-F5EYMVQM.js.map +1 -0
- package/libs/chunk-FVROL3V5.js +9 -0
- package/libs/chunk-FVROL3V5.js.map +1 -0
- package/libs/chunk-GT77BX4L.cjs +17 -0
- package/libs/chunk-GT77BX4L.cjs.map +1 -0
- package/libs/chunk-GUJSMQ3V.cjs +16 -0
- package/libs/chunk-GUJSMQ3V.cjs.map +1 -0
- package/libs/chunk-HHLNOC5T.js +7 -0
- package/libs/chunk-HHLNOC5T.js.map +1 -0
- package/libs/chunk-HRRHPLER.js +8 -0
- package/libs/chunk-HRRHPLER.js.map +1 -0
- package/libs/chunk-IEB64SWY.js +8 -0
- package/libs/chunk-IEB64SWY.js.map +1 -0
- package/libs/{chunk-NGTJDDFO.js → chunk-IQ76HGVP.js} +2 -2
- package/libs/chunk-IRLFZ3OL.js +9 -0
- package/libs/chunk-IRLFZ3OL.js.map +1 -0
- package/libs/{chunk-JJ43O4Y5.js → chunk-KK47SYZI.js} +2 -2
- package/libs/chunk-O3JIHC5M.cjs +15 -0
- package/libs/chunk-O3JIHC5M.cjs.map +1 -0
- package/libs/chunk-O5XAJ7BY.cjs +18 -0
- package/libs/chunk-O5XAJ7BY.cjs.map +1 -0
- package/libs/chunk-OVWLQYMK.js +10 -0
- package/libs/chunk-OVWLQYMK.js.map +1 -0
- package/libs/chunk-PNWIRCG3.cjs +7 -0
- package/libs/chunk-PNWIRCG3.cjs.map +1 -0
- package/libs/{chunk-D4YLRWAO.cjs → chunk-QVW6W76L.cjs} +6 -6
- package/libs/chunk-T4T6GWYQ.cjs +17 -0
- package/libs/chunk-T4T6GWYQ.cjs.map +1 -0
- package/libs/chunk-TON2YGMD.cjs +9 -0
- package/libs/chunk-TON2YGMD.cjs.map +1 -0
- package/libs/chunk-UEPAWMDF.js +8 -0
- package/libs/chunk-UEPAWMDF.js.map +1 -0
- package/libs/{chunk-LT5KZ2QW.cjs → chunk-US2I5GI7.cjs} +3 -3
- package/libs/{chunk-B7F5FS6D.cjs → chunk-W2UIN7EV.cjs} +3 -3
- package/libs/{chunk-P2DC76ZZ.cjs → chunk-W5TKWBFC.cjs} +3 -3
- package/libs/chunk-WXBFBWYF.cjs +16 -0
- package/libs/chunk-WXBFBWYF.cjs.map +1 -0
- package/libs/{chunk-VUH3FXGJ.js → chunk-X3JCTEPD.js} +5 -5
- package/libs/chunk-X5LGFCWG.js +9 -0
- package/libs/chunk-X5LGFCWG.js.map +1 -0
- package/libs/{chunk-5M57K4SW.js → chunk-Y2PFDELK.js} +2 -2
- package/libs/{chunk-ETFLFC2S.js → chunk-ZFJ4U45S.js} +2 -2
- package/libs/{component-props-a8a2f97e.d.ts → component-props-67d978a2.d.ts} +4 -4
- package/libs/components/alert/alert.css +1 -1
- package/libs/components/alert/alert.css.map +1 -1
- package/libs/components/alert/alert.min.css +2 -2
- package/libs/components/breadcrumbs/breadcrumb.cjs +6 -6
- package/libs/components/breadcrumbs/breadcrumb.d.cts +11 -11
- package/libs/components/breadcrumbs/breadcrumb.d.ts +11 -11
- package/libs/components/breadcrumbs/breadcrumb.js +3 -3
- package/libs/components/button.cjs +6 -4
- package/libs/components/button.d.cts +97 -4
- package/libs/components/button.d.ts +97 -4
- package/libs/components/button.js +4 -2
- package/libs/components/card.cjs +7 -7
- package/libs/components/card.d.cts +14 -14
- package/libs/components/card.d.ts +14 -14
- package/libs/components/card.js +2 -2
- package/libs/components/dialog/dialog.cjs +9 -7
- package/libs/components/dialog/dialog.d.cts +3 -3
- package/libs/components/dialog/dialog.d.ts +3 -3
- package/libs/components/dialog/dialog.js +7 -5
- package/libs/components/form/fields.cjs +4 -4
- package/libs/components/form/fields.d.cts +16 -7
- package/libs/components/form/fields.d.ts +16 -7
- package/libs/components/form/fields.js +2 -2
- package/libs/components/form/inputs.cjs +6 -4
- package/libs/components/form/inputs.d.cts +50 -2
- package/libs/components/form/inputs.d.ts +50 -2
- package/libs/components/form/inputs.js +4 -2
- package/libs/components/form/textarea.cjs +5 -4
- package/libs/components/form/textarea.d.cts +32 -23
- package/libs/components/form/textarea.d.ts +32 -23
- package/libs/components/form/textarea.js +3 -2
- package/libs/components/heading/heading.cjs +3 -3
- package/libs/components/heading/heading.d.cts +2 -2
- package/libs/components/heading/heading.d.ts +2 -2
- package/libs/components/heading/heading.js +2 -2
- package/libs/components/icons/icon.cjs +4 -4
- package/libs/components/icons/icon.d.cts +38 -38
- package/libs/components/icons/icon.d.ts +38 -38
- package/libs/components/icons/icon.js +2 -2
- package/libs/components/link/link.cjs +4 -4
- package/libs/components/link/link.css +1 -1
- package/libs/components/link/link.css.map +1 -1
- package/libs/components/link/link.d.cts +3 -19
- package/libs/components/link/link.d.ts +3 -19
- package/libs/components/link/link.js +2 -2
- package/libs/components/link/link.min.css +2 -2
- package/libs/components/list/list.cjs +5 -5
- package/libs/components/list/list.css +1 -0
- package/libs/components/list/list.css.map +1 -0
- package/libs/components/list/list.d.cts +120 -33
- package/libs/components/list/list.d.ts +120 -33
- package/libs/components/list/list.js +2 -2
- package/libs/components/list/list.min.css +3 -0
- package/libs/components/modal.cjs +6 -4
- package/libs/components/modal.d.cts +8 -8
- package/libs/components/modal.d.ts +8 -8
- package/libs/components/modal.js +5 -3
- package/libs/components/nav/nav.cjs +7 -7
- package/libs/components/nav/nav.css +1 -1
- package/libs/components/nav/nav.css.map +1 -1
- package/libs/components/nav/nav.d.cts +550 -34
- package/libs/components/nav/nav.d.ts +550 -34
- package/libs/components/nav/nav.js +3 -3
- package/libs/components/nav/nav.min.css +2 -2
- package/libs/components/popover/popover.d.cts +5 -5
- package/libs/components/popover/popover.d.ts +5 -5
- package/libs/components/tables/table.cjs +5 -5
- package/libs/components/tables/table.d.cts +8 -8
- package/libs/components/tables/table.d.ts +8 -8
- package/libs/components/tables/table.js +2 -2
- package/libs/components/tag/tag.css +1 -1
- package/libs/components/tag/tag.css.map +1 -1
- package/libs/components/tag/tag.min.css +2 -2
- package/libs/components/text/text.cjs +5 -5
- package/libs/components/text/text.d.cts +5 -5
- package/libs/components/text/text.d.ts +5 -5
- package/libs/components/text/text.js +2 -2
- package/libs/form.types-d25ebfac.d.ts +233 -0
- package/libs/{heading-3648c538.d.ts → heading-7446cb46.d.ts} +8 -8
- package/libs/hooks.cjs +9 -4
- package/libs/hooks.d.cts +137 -3
- package/libs/hooks.d.ts +137 -3
- package/libs/hooks.js +4 -3
- package/libs/icons.cjs +3 -3
- package/libs/icons.d.cts +2 -2
- package/libs/icons.d.ts +2 -2
- package/libs/icons.js +2 -2
- package/libs/index.cjs +53 -51
- 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 +338 -49
- package/libs/index.d.ts +338 -49
- package/libs/index.js +24 -22
- package/libs/index.js.map +1 -1
- package/libs/link-5192f411.d.ts +323 -0
- package/libs/list.types-d26de310.d.ts +245 -0
- package/libs/{ui-645f95b5.d.ts → ui-d01b50d4.d.ts} +16 -12
- package/package.json +4 -6
- package/src/components/alert/alert.scss +1 -4
- package/src/components/breadcrumbs/breadcrumb.tsx +4 -1
- package/src/components/buttons/README.mdx +102 -1
- package/src/components/buttons/button.stories.tsx +106 -0
- package/src/components/buttons/button.tsx +82 -52
- package/src/components/dialog/dialog-a11y-review.md +653 -0
- package/src/components/form/README.mdx +725 -43
- package/src/components/form/WCAG-REVIEW.md +654 -0
- package/src/components/form/fields.tsx +10 -1
- package/src/components/form/form.stories.tsx +604 -23
- package/src/components/form/form.tsx +204 -63
- package/src/components/form/form.types.ts +378 -0
- package/src/components/form/input.stories.tsx +71 -3
- package/src/components/form/inputs.tsx +159 -67
- package/src/components/form/select.tsx +122 -66
- package/src/components/form/textarea.tsx +120 -73
- package/src/components/fp.tsx +86 -11
- package/src/components/link/README.mdx +923 -0
- package/src/components/link/link.scss +79 -26
- package/src/components/link/link.stories.tsx +383 -30
- package/src/components/link/link.test.tsx +677 -0
- package/src/components/link/link.tsx +163 -57
- package/src/components/link/link.types.ts +261 -0
- package/src/components/list/README.mdx +764 -0
- package/src/components/list/list.scss +285 -0
- package/src/components/list/list.stories.tsx +514 -27
- package/src/components/list/list.test.tsx +554 -0
- package/src/components/list/list.tsx +153 -51
- package/src/components/list/list.types.ts +255 -0
- package/src/components/nav/ACCESSIBILITY.md +649 -0
- package/src/components/nav/README.mdx +782 -0
- package/src/components/nav/nav.scss +32 -1
- package/src/components/nav/nav.stories.tsx +44 -6
- package/src/components/nav/nav.tsx +302 -51
- package/src/components/nav/nav.types.ts +308 -0
- package/src/components/tag/README.mdx +426 -0
- package/src/components/tag/tag.scss +101 -27
- package/src/components/tag/tag.stories.tsx +384 -10
- package/src/components/tag/tag.test.tsx +210 -0
- package/src/components/tag/tag.tsx +106 -9
- package/src/components/tag/tag.types.ts +107 -0
- package/src/components/ui.tsx +8 -3
- package/src/hooks/use-disabled-state.test.tsx +536 -0
- package/src/hooks/use-disabled-state.ts +246 -0
- package/src/hooks/useDisabledState.md +393 -0
- package/src/hooks.ts +6 -0
- package/src/index.scss +2 -0
- package/src/index.ts +2 -1
- package/src/sass/_globals.scss +2 -7
- package/src/styles/alert/alert.css +1 -3
- package/src/styles/alert/alert.css.map +1 -1
- package/src/styles/index.css +450 -76
- package/src/styles/index.css.map +1 -1
- package/src/styles/link/link.css +45 -28
- package/src/styles/link/link.css.map +1 -1
- package/src/styles/list/list.css +214 -0
- package/src/styles/list/list.css.map +1 -0
- package/src/styles/nav/nav.css +21 -1
- package/src/styles/nav/nav.css.map +1 -1
- package/src/styles/tag/tag.css +113 -35
- package/src/styles/tag/tag.css.map +1 -1
- package/src/styles/utilities/_disabled.scss +58 -0
- package/src/types/shared.ts +43 -6
- package/src/utils/accessibility.ts +109 -0
- package/libs/chunk-2LTJ7HHX.cjs +0 -18
- package/libs/chunk-2LTJ7HHX.cjs.map +0 -1
- package/libs/chunk-2Y7W75TT.js +0 -9
- package/libs/chunk-2Y7W75TT.js.map +0 -1
- package/libs/chunk-5S4ORA4C.cjs +0 -15
- package/libs/chunk-5S4ORA4C.cjs.map +0 -1
- package/libs/chunk-AHDJGCG5.cjs +0 -15
- package/libs/chunk-AHDJGCG5.cjs.map +0 -1
- package/libs/chunk-BHRQBJRY.js +0 -8
- package/libs/chunk-BHRQBJRY.js.map +0 -1
- package/libs/chunk-GZ4QFPRY.js +0 -9
- package/libs/chunk-GZ4QFPRY.js.map +0 -1
- package/libs/chunk-IYUN2EW3.cjs +0 -15
- package/libs/chunk-IYUN2EW3.cjs.map +0 -1
- package/libs/chunk-J32EZPYD.cjs +0 -15
- package/libs/chunk-J32EZPYD.cjs.map +0 -1
- package/libs/chunk-KUKIVRC2.js +0 -7
- package/libs/chunk-KUKIVRC2.js.map +0 -1
- package/libs/chunk-L75OQKEI.cjs.map +0 -1
- package/libs/chunk-M5RRNTVX.cjs +0 -15
- package/libs/chunk-M5RRNTVX.cjs.map +0 -1
- package/libs/chunk-OK5QEIMD.cjs +0 -17
- package/libs/chunk-OK5QEIMD.cjs.map +0 -1
- package/libs/chunk-P7TTEYCD.js +0 -7
- package/libs/chunk-P7TTEYCD.js.map +0 -1
- package/libs/chunk-QLZWHAMK.js +0 -8
- package/libs/chunk-QLZWHAMK.js.map +0 -1
- package/libs/chunk-RIVUMPOG.js +0 -8
- package/libs/chunk-RIVUMPOG.js.map +0 -1
- package/libs/chunk-S7BABR7Z.cjs +0 -13
- package/libs/chunk-S7BABR7Z.cjs.map +0 -1
- package/libs/chunk-SMYRLO3E.js +0 -8
- package/libs/chunk-SMYRLO3E.js.map +0 -1
- package/libs/chunk-TYRCEX2L.js +0 -8
- package/libs/chunk-TYRCEX2L.js.map +0 -1
- package/libs/chunk-XBA562WW.js +0 -8
- package/libs/chunk-XBA562WW.js.map +0 -1
- package/libs/chunk-XTQKWY7W.cjs +0 -32
- package/libs/chunk-XTQKWY7W.cjs.map +0 -1
- package/libs/inputs-f3a216db.d.ts +0 -45
- /package/libs/{chunk-PQ2K3BM6.cjs.map → chunk-2NRIP6RB.cjs.map} +0 -0
- /package/libs/{chunk-772NRB75.js.map → chunk-5QD3DWFI.js.map} +0 -0
- /package/libs/{chunk-3MKLDCKQ.cjs.map → chunk-6WTC4JXH.cjs.map} +0 -0
- /package/libs/{chunk-ZANSFMTD.js.map → chunk-7XPFW7CB.js.map} +0 -0
- /package/libs/{chunk-ROZI23GS.cjs.map → chunk-DKTHCQ5P.cjs.map} +0 -0
- /package/libs/{chunk-NGTJDDFO.js.map → chunk-IQ76HGVP.js.map} +0 -0
- /package/libs/{chunk-JJ43O4Y5.js.map → chunk-KK47SYZI.js.map} +0 -0
- /package/libs/{chunk-D4YLRWAO.cjs.map → chunk-QVW6W76L.cjs.map} +0 -0
- /package/libs/{chunk-LT5KZ2QW.cjs.map → chunk-US2I5GI7.cjs.map} +0 -0
- /package/libs/{chunk-B7F5FS6D.cjs.map → chunk-W2UIN7EV.cjs.map} +0 -0
- /package/libs/{chunk-P2DC76ZZ.cjs.map → chunk-W5TKWBFC.cjs.map} +0 -0
- /package/libs/{chunk-VUH3FXGJ.js.map → chunk-X3JCTEPD.js.map} +0 -0
- /package/libs/{chunk-5M57K4SW.js.map → chunk-Y2PFDELK.js.map} +0 -0
- /package/libs/{chunk-ETFLFC2S.js.map → chunk-ZFJ4U45S.js.map} +0 -0
|
@@ -0,0 +1,554 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { render, screen } from "@testing-library/react";
|
|
3
|
+
import { describe, it, expect, vi } from "vitest";
|
|
4
|
+
import List from "./list";
|
|
5
|
+
|
|
6
|
+
describe("List Component", () => {
|
|
7
|
+
describe("Basic Rendering", () => {
|
|
8
|
+
it("renders an unordered list by default", () => {
|
|
9
|
+
render(
|
|
10
|
+
<List>
|
|
11
|
+
<List.ListItem>Item 1</List.ListItem>
|
|
12
|
+
<List.ListItem>Item 2</List.ListItem>
|
|
13
|
+
</List>
|
|
14
|
+
);
|
|
15
|
+
|
|
16
|
+
const list = screen.getByRole("list");
|
|
17
|
+
expect(list.tagName).toBe("UL");
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it("renders an ordered list when type='ol'", () => {
|
|
21
|
+
render(
|
|
22
|
+
<List type="ol">
|
|
23
|
+
<List.ListItem>First</List.ListItem>
|
|
24
|
+
<List.ListItem>Second</List.ListItem>
|
|
25
|
+
</List>
|
|
26
|
+
);
|
|
27
|
+
|
|
28
|
+
const list = screen.getByRole("list");
|
|
29
|
+
expect(list.tagName).toBe("OL");
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it("renders a definition list when type='dl'", () => {
|
|
33
|
+
render(
|
|
34
|
+
<List type="dl">
|
|
35
|
+
<List.ListItem type="dt">Term</List.ListItem>
|
|
36
|
+
<List.ListItem type="dd">Definition</List.ListItem>
|
|
37
|
+
</List>
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
const term = screen.getByText("Term");
|
|
41
|
+
const definition = screen.getByText("Definition");
|
|
42
|
+
|
|
43
|
+
expect(term.tagName).toBe("DT");
|
|
44
|
+
expect(definition.tagName).toBe("DD");
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it("renders children correctly", () => {
|
|
48
|
+
render(
|
|
49
|
+
<List>
|
|
50
|
+
<List.ListItem>Test Item 1</List.ListItem>
|
|
51
|
+
<List.ListItem>Test Item 2</List.ListItem>
|
|
52
|
+
<List.ListItem>Test Item 3</List.ListItem>
|
|
53
|
+
</List>
|
|
54
|
+
);
|
|
55
|
+
|
|
56
|
+
expect(screen.getByText("Test Item 1")).toBeInTheDocument();
|
|
57
|
+
expect(screen.getByText("Test Item 2")).toBeInTheDocument();
|
|
58
|
+
expect(screen.getByText("Test Item 3")).toBeInTheDocument();
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
describe("ListItem Component", () => {
|
|
63
|
+
it("renders a list item (li) by default", () => {
|
|
64
|
+
render(
|
|
65
|
+
<List>
|
|
66
|
+
<List.ListItem>Default item</List.ListItem>
|
|
67
|
+
</List>
|
|
68
|
+
);
|
|
69
|
+
|
|
70
|
+
const item = screen.getByText("Default item");
|
|
71
|
+
expect(item.tagName).toBe("LI");
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
it("renders a definition term (dt) when type='dt'", () => {
|
|
75
|
+
render(
|
|
76
|
+
<List type="dl">
|
|
77
|
+
<List.ListItem type="dt">Term</List.ListItem>
|
|
78
|
+
</List>
|
|
79
|
+
);
|
|
80
|
+
|
|
81
|
+
const item = screen.getByText("Term");
|
|
82
|
+
expect(item.tagName).toBe("DT");
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
it("renders a definition description (dd) when type='dd'", () => {
|
|
86
|
+
render(
|
|
87
|
+
<List type="dl">
|
|
88
|
+
<List.ListItem type="dd">Description</List.ListItem>
|
|
89
|
+
</List>
|
|
90
|
+
);
|
|
91
|
+
|
|
92
|
+
const item = screen.getByText("Description");
|
|
93
|
+
expect(item.tagName).toBe("DD");
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
it("accepts custom id prop", () => {
|
|
97
|
+
render(
|
|
98
|
+
<List>
|
|
99
|
+
<List.ListItem id="custom-item">Item with ID</List.ListItem>
|
|
100
|
+
</List>
|
|
101
|
+
);
|
|
102
|
+
|
|
103
|
+
const item = screen.getByText("Item with ID");
|
|
104
|
+
expect(item).toHaveAttribute("id", "custom-item");
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
it("accepts custom classes prop", () => {
|
|
108
|
+
render(
|
|
109
|
+
<List>
|
|
110
|
+
<List.ListItem classes="custom-class">Styled item</List.ListItem>
|
|
111
|
+
</List>
|
|
112
|
+
);
|
|
113
|
+
|
|
114
|
+
const item = screen.getByText("Styled item");
|
|
115
|
+
expect(item).toHaveClass("custom-class");
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
it("accepts custom styles prop", () => {
|
|
119
|
+
render(
|
|
120
|
+
<List>
|
|
121
|
+
<List.ListItem styles={{ paddingLeft: "1rem" }}>Styled item</List.ListItem>
|
|
122
|
+
</List>
|
|
123
|
+
);
|
|
124
|
+
|
|
125
|
+
const item = screen.getByText("Styled item");
|
|
126
|
+
expect(item).toHaveStyle({ paddingLeft: "1rem" });
|
|
127
|
+
});
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
describe("Props and Attributes", () => {
|
|
131
|
+
it("applies variant via data-variant attribute", () => {
|
|
132
|
+
render(
|
|
133
|
+
<List variant="inline">
|
|
134
|
+
<List.ListItem>Item</List.ListItem>
|
|
135
|
+
</List>
|
|
136
|
+
);
|
|
137
|
+
|
|
138
|
+
const list = screen.getByRole("list");
|
|
139
|
+
expect(list).toHaveAttribute("data-variant", "inline");
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
it("applies custom CSS classes", () => {
|
|
143
|
+
render(
|
|
144
|
+
<List classes="custom-list-class">
|
|
145
|
+
<List.ListItem>Item</List.ListItem>
|
|
146
|
+
</List>
|
|
147
|
+
);
|
|
148
|
+
|
|
149
|
+
const list = screen.getByRole("list");
|
|
150
|
+
expect(list).toHaveClass("custom-list-class");
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
it("applies inline styles", () => {
|
|
154
|
+
render(
|
|
155
|
+
<List styles={{ marginTop: "2rem" }}>
|
|
156
|
+
<List.ListItem>Item</List.ListItem>
|
|
157
|
+
</List>
|
|
158
|
+
);
|
|
159
|
+
|
|
160
|
+
const list = screen.getByRole("list");
|
|
161
|
+
expect(list).toHaveStyle({ marginTop: "2rem" });
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
it("applies custom id", () => {
|
|
165
|
+
render(
|
|
166
|
+
<List id="main-list">
|
|
167
|
+
<List.ListItem>Item</List.ListItem>
|
|
168
|
+
</List>
|
|
169
|
+
);
|
|
170
|
+
|
|
171
|
+
const list = screen.getByRole("list");
|
|
172
|
+
expect(list).toHaveAttribute("id", "main-list");
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
it("supports role override for accessibility", () => {
|
|
176
|
+
render(
|
|
177
|
+
<List variant="none" role="list">
|
|
178
|
+
<List.ListItem>Item</List.ListItem>
|
|
179
|
+
</List>
|
|
180
|
+
);
|
|
181
|
+
|
|
182
|
+
const list = screen.getByRole("list");
|
|
183
|
+
expect(list).toHaveAttribute("role", "list");
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
it("supports aria-label", () => {
|
|
187
|
+
render(
|
|
188
|
+
<List aria-label="Navigation menu">
|
|
189
|
+
<List.ListItem>Home</List.ListItem>
|
|
190
|
+
</List>
|
|
191
|
+
);
|
|
192
|
+
|
|
193
|
+
const list = screen.getByRole("list", { name: "Navigation menu" });
|
|
194
|
+
expect(list).toBeInTheDocument();
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
it("supports aria-labelledby", () => {
|
|
198
|
+
render(
|
|
199
|
+
<div>
|
|
200
|
+
<h2 id="list-heading">Features</h2>
|
|
201
|
+
<List aria-labelledby="list-heading">
|
|
202
|
+
<List.ListItem>Feature 1</List.ListItem>
|
|
203
|
+
</List>
|
|
204
|
+
</div>
|
|
205
|
+
);
|
|
206
|
+
|
|
207
|
+
const list = screen.getByRole("list");
|
|
208
|
+
expect(list).toHaveAttribute("aria-labelledby", "list-heading");
|
|
209
|
+
});
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
describe("Nested Lists", () => {
|
|
213
|
+
it("renders nested unordered lists", () => {
|
|
214
|
+
render(
|
|
215
|
+
<List>
|
|
216
|
+
<List.ListItem>
|
|
217
|
+
Parent item
|
|
218
|
+
<List>
|
|
219
|
+
<List.ListItem>Child item 1</List.ListItem>
|
|
220
|
+
<List.ListItem>Child item 2</List.ListItem>
|
|
221
|
+
</List>
|
|
222
|
+
</List.ListItem>
|
|
223
|
+
</List>
|
|
224
|
+
);
|
|
225
|
+
|
|
226
|
+
const lists = screen.getAllByRole("list");
|
|
227
|
+
expect(lists).toHaveLength(2); // Parent and nested list
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
it("renders nested ordered lists", () => {
|
|
231
|
+
render(
|
|
232
|
+
<List type="ol">
|
|
233
|
+
<List.ListItem>
|
|
234
|
+
Step 1
|
|
235
|
+
<List type="ol">
|
|
236
|
+
<List.ListItem>Sub-step 1.1</List.ListItem>
|
|
237
|
+
<List.ListItem>Sub-step 1.2</List.ListItem>
|
|
238
|
+
</List>
|
|
239
|
+
</List.ListItem>
|
|
240
|
+
</List>
|
|
241
|
+
);
|
|
242
|
+
|
|
243
|
+
const lists = screen.getAllByRole("list");
|
|
244
|
+
expect(lists).toHaveLength(2);
|
|
245
|
+
lists.forEach((list) => {
|
|
246
|
+
expect(list.tagName).toBe("OL");
|
|
247
|
+
});
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
it("renders mixed nested list types", () => {
|
|
251
|
+
render(
|
|
252
|
+
<List type="ul">
|
|
253
|
+
<List.ListItem>
|
|
254
|
+
Parent
|
|
255
|
+
<List type="ol">
|
|
256
|
+
<List.ListItem>Nested ordered</List.ListItem>
|
|
257
|
+
</List>
|
|
258
|
+
</List.ListItem>
|
|
259
|
+
</List>
|
|
260
|
+
);
|
|
261
|
+
|
|
262
|
+
const lists = screen.getAllByRole("list");
|
|
263
|
+
expect(lists).toHaveLength(2);
|
|
264
|
+
expect(lists[0].tagName).toBe("UL");
|
|
265
|
+
expect(lists[1].tagName).toBe("OL");
|
|
266
|
+
});
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
describe("Ref Forwarding", () => {
|
|
270
|
+
it("forwards ref to List component", () => {
|
|
271
|
+
const ref = vi.fn();
|
|
272
|
+
|
|
273
|
+
render(
|
|
274
|
+
<List ref={ref}>
|
|
275
|
+
<List.ListItem>Item</List.ListItem>
|
|
276
|
+
</List>
|
|
277
|
+
);
|
|
278
|
+
|
|
279
|
+
expect(ref).toHaveBeenCalled();
|
|
280
|
+
const refValue = ref.mock.calls[0][0];
|
|
281
|
+
expect(refValue).toBeInstanceOf(HTMLUListElement);
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
it("forwards ref to ordered list", () => {
|
|
285
|
+
const ref = vi.fn();
|
|
286
|
+
|
|
287
|
+
render(
|
|
288
|
+
<List type="ol" ref={ref}>
|
|
289
|
+
<List.ListItem>Item</List.ListItem>
|
|
290
|
+
</List>
|
|
291
|
+
);
|
|
292
|
+
|
|
293
|
+
expect(ref).toHaveBeenCalled();
|
|
294
|
+
const refValue = ref.mock.calls[0][0];
|
|
295
|
+
expect(refValue).toBeInstanceOf(HTMLOListElement);
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
it("forwards ref to definition list", () => {
|
|
299
|
+
const ref = vi.fn();
|
|
300
|
+
|
|
301
|
+
render(
|
|
302
|
+
<List type="dl" ref={ref}>
|
|
303
|
+
<List.ListItem type="dt">Term</List.ListItem>
|
|
304
|
+
</List>
|
|
305
|
+
);
|
|
306
|
+
|
|
307
|
+
expect(ref).toHaveBeenCalled();
|
|
308
|
+
const refValue = ref.mock.calls[0][0];
|
|
309
|
+
expect(refValue).toBeInstanceOf(HTMLDListElement);
|
|
310
|
+
});
|
|
311
|
+
|
|
312
|
+
it("forwards ref to ListItem", () => {
|
|
313
|
+
const ref = vi.fn();
|
|
314
|
+
|
|
315
|
+
render(
|
|
316
|
+
<List>
|
|
317
|
+
<List.ListItem ref={ref}>Item</List.ListItem>
|
|
318
|
+
</List>
|
|
319
|
+
);
|
|
320
|
+
|
|
321
|
+
expect(ref).toHaveBeenCalled();
|
|
322
|
+
const refValue = ref.mock.calls[0][0];
|
|
323
|
+
expect(refValue).toBeInstanceOf(HTMLLIElement);
|
|
324
|
+
});
|
|
325
|
+
});
|
|
326
|
+
|
|
327
|
+
describe("Compound Component Pattern", () => {
|
|
328
|
+
it("List.ListItem is attached to List", () => {
|
|
329
|
+
expect(List.ListItem).toBeDefined();
|
|
330
|
+
expect(typeof List.ListItem).toBe("object"); // forwardRef returns an object
|
|
331
|
+
});
|
|
332
|
+
|
|
333
|
+
it("works with List.ListItem syntax", () => {
|
|
334
|
+
render(
|
|
335
|
+
<List>
|
|
336
|
+
<List.ListItem>Item 1</List.ListItem>
|
|
337
|
+
<List.ListItem>Item 2</List.ListItem>
|
|
338
|
+
</List>
|
|
339
|
+
);
|
|
340
|
+
|
|
341
|
+
expect(screen.getByText("Item 1")).toBeInTheDocument();
|
|
342
|
+
expect(screen.getByText("Item 2")).toBeInTheDocument();
|
|
343
|
+
});
|
|
344
|
+
});
|
|
345
|
+
|
|
346
|
+
describe("Display Names", () => {
|
|
347
|
+
it("List has correct displayName", () => {
|
|
348
|
+
expect(List.displayName).toBe("List");
|
|
349
|
+
});
|
|
350
|
+
|
|
351
|
+
it("ListItem has correct displayName", () => {
|
|
352
|
+
expect(List.ListItem.displayName).toBe("ListItem");
|
|
353
|
+
});
|
|
354
|
+
});
|
|
355
|
+
|
|
356
|
+
describe("Accessibility", () => {
|
|
357
|
+
it("renders with proper ARIA attributes", () => {
|
|
358
|
+
render(
|
|
359
|
+
<List aria-label="Product features">
|
|
360
|
+
<List.ListItem>Feature 1</List.ListItem>
|
|
361
|
+
</List>
|
|
362
|
+
);
|
|
363
|
+
|
|
364
|
+
const list = screen.getByRole("list", { name: "Product features" });
|
|
365
|
+
expect(list).toBeInTheDocument();
|
|
366
|
+
});
|
|
367
|
+
|
|
368
|
+
it("supports role override for styled lists", () => {
|
|
369
|
+
render(
|
|
370
|
+
<List variant="none" role="list">
|
|
371
|
+
<List.ListItem>Item 1</List.ListItem>
|
|
372
|
+
</List>
|
|
373
|
+
);
|
|
374
|
+
|
|
375
|
+
const list = screen.getByRole("list");
|
|
376
|
+
expect(list).toHaveAttribute("role", "list");
|
|
377
|
+
});
|
|
378
|
+
|
|
379
|
+
it("maintains semantic structure with nested lists", () => {
|
|
380
|
+
render(
|
|
381
|
+
<List aria-label="Parent list">
|
|
382
|
+
<List.ListItem>
|
|
383
|
+
Parent
|
|
384
|
+
<List>
|
|
385
|
+
<List.ListItem>Nested item</List.ListItem>
|
|
386
|
+
</List>
|
|
387
|
+
</List.ListItem>
|
|
388
|
+
</List>
|
|
389
|
+
);
|
|
390
|
+
|
|
391
|
+
const lists = screen.getAllByRole("list");
|
|
392
|
+
expect(lists).toHaveLength(2); // Parent and nested list
|
|
393
|
+
});
|
|
394
|
+
|
|
395
|
+
it("renders definition lists with proper semantics", () => {
|
|
396
|
+
render(
|
|
397
|
+
<List type="dl">
|
|
398
|
+
<List.ListItem type="dt">Term</List.ListItem>
|
|
399
|
+
<List.ListItem type="dd">Definition</List.ListItem>
|
|
400
|
+
</List>
|
|
401
|
+
);
|
|
402
|
+
|
|
403
|
+
const term = screen.getByText("Term");
|
|
404
|
+
const definition = screen.getByText("Definition");
|
|
405
|
+
|
|
406
|
+
expect(term.tagName).toBe("DT");
|
|
407
|
+
expect(definition.tagName).toBe("DD");
|
|
408
|
+
});
|
|
409
|
+
});
|
|
410
|
+
|
|
411
|
+
describe("Edge Cases", () => {
|
|
412
|
+
it("renders with no children", () => {
|
|
413
|
+
// @ts-expect-error - Testing edge case where children is missing
|
|
414
|
+
render(<List />);
|
|
415
|
+
const list = screen.getByRole("list");
|
|
416
|
+
expect(list).toBeInTheDocument();
|
|
417
|
+
expect(list).toBeEmptyDOMElement();
|
|
418
|
+
});
|
|
419
|
+
|
|
420
|
+
it("renders with single child", () => {
|
|
421
|
+
render(
|
|
422
|
+
<List>
|
|
423
|
+
<List.ListItem>Single item</List.ListItem>
|
|
424
|
+
</List>
|
|
425
|
+
);
|
|
426
|
+
|
|
427
|
+
expect(screen.getByText("Single item")).toBeInTheDocument();
|
|
428
|
+
});
|
|
429
|
+
|
|
430
|
+
it("renders with many children", () => {
|
|
431
|
+
const items = Array.from({ length: 100 }, (_, i) => `Item ${i + 1}`);
|
|
432
|
+
|
|
433
|
+
render(
|
|
434
|
+
<List>
|
|
435
|
+
{items.map((item) => (
|
|
436
|
+
<List.ListItem key={item}>{item}</List.ListItem>
|
|
437
|
+
))}
|
|
438
|
+
</List>
|
|
439
|
+
);
|
|
440
|
+
|
|
441
|
+
expect(screen.getByText("Item 1")).toBeInTheDocument();
|
|
442
|
+
expect(screen.getByText("Item 100")).toBeInTheDocument();
|
|
443
|
+
});
|
|
444
|
+
|
|
445
|
+
it("handles complex children", () => {
|
|
446
|
+
render(
|
|
447
|
+
<List>
|
|
448
|
+
<List.ListItem>
|
|
449
|
+
<strong>Bold</strong> and <em>italic</em> text
|
|
450
|
+
</List.ListItem>
|
|
451
|
+
</List>
|
|
452
|
+
);
|
|
453
|
+
|
|
454
|
+
expect(screen.getByText("Bold")).toBeInTheDocument();
|
|
455
|
+
expect(screen.getByText("italic")).toBeInTheDocument();
|
|
456
|
+
});
|
|
457
|
+
|
|
458
|
+
it("spreads additional props to list", () => {
|
|
459
|
+
render(
|
|
460
|
+
<List data-testid="custom-list" data-custom="value">
|
|
461
|
+
<List.ListItem>Item</List.ListItem>
|
|
462
|
+
</List>
|
|
463
|
+
);
|
|
464
|
+
|
|
465
|
+
const list = screen.getByTestId("custom-list");
|
|
466
|
+
expect(list).toHaveAttribute("data-custom", "value");
|
|
467
|
+
});
|
|
468
|
+
|
|
469
|
+
it("spreads additional props to list item", () => {
|
|
470
|
+
render(
|
|
471
|
+
<List>
|
|
472
|
+
<List.ListItem data-testid="custom-item" data-custom="value">
|
|
473
|
+
Item
|
|
474
|
+
</List.ListItem>
|
|
475
|
+
</List>
|
|
476
|
+
);
|
|
477
|
+
|
|
478
|
+
const item = screen.getByTestId("custom-item");
|
|
479
|
+
expect(item).toHaveAttribute("data-custom", "value");
|
|
480
|
+
});
|
|
481
|
+
});
|
|
482
|
+
|
|
483
|
+
describe("Real-World Use Cases", () => {
|
|
484
|
+
it("renders a navigation list", () => {
|
|
485
|
+
render(
|
|
486
|
+
<nav>
|
|
487
|
+
<List variant="inline" role="list" aria-label="Main navigation">
|
|
488
|
+
<List.ListItem>
|
|
489
|
+
<a href="/">Home</a>
|
|
490
|
+
</List.ListItem>
|
|
491
|
+
<List.ListItem>
|
|
492
|
+
<a href="/about">About</a>
|
|
493
|
+
</List.ListItem>
|
|
494
|
+
<List.ListItem>
|
|
495
|
+
<a href="/contact">Contact</a>
|
|
496
|
+
</List.ListItem>
|
|
497
|
+
</List>
|
|
498
|
+
</nav>
|
|
499
|
+
);
|
|
500
|
+
|
|
501
|
+
const list = screen.getByRole("list", { name: "Main navigation" });
|
|
502
|
+
expect(list).toHaveAttribute("data-variant", "inline");
|
|
503
|
+
});
|
|
504
|
+
|
|
505
|
+
it("renders a steps list", () => {
|
|
506
|
+
render(
|
|
507
|
+
<List type="ol" aria-label="Installation steps">
|
|
508
|
+
<List.ListItem>Download the package</List.ListItem>
|
|
509
|
+
<List.ListItem>Extract the files</List.ListItem>
|
|
510
|
+
<List.ListItem>Run the installer</List.ListItem>
|
|
511
|
+
</List>
|
|
512
|
+
);
|
|
513
|
+
|
|
514
|
+
const list = screen.getByRole("list", { name: "Installation steps" });
|
|
515
|
+
expect(list.tagName).toBe("OL");
|
|
516
|
+
});
|
|
517
|
+
|
|
518
|
+
it("renders a glossary", () => {
|
|
519
|
+
render(
|
|
520
|
+
<List type="dl">
|
|
521
|
+
<List.ListItem type="dt">HTML</List.ListItem>
|
|
522
|
+
<List.ListItem type="dd">HyperText Markup Language</List.ListItem>
|
|
523
|
+
<List.ListItem type="dt">CSS</List.ListItem>
|
|
524
|
+
<List.ListItem type="dd">Cascading Style Sheets</List.ListItem>
|
|
525
|
+
</List>
|
|
526
|
+
);
|
|
527
|
+
|
|
528
|
+
expect(screen.getByText("HTML")).toBeInTheDocument();
|
|
529
|
+
expect(screen.getByText("HyperText Markup Language")).toBeInTheDocument();
|
|
530
|
+
});
|
|
531
|
+
|
|
532
|
+
it("renders a feature list with custom styling", () => {
|
|
533
|
+
render(
|
|
534
|
+
<List
|
|
535
|
+
variant="custom"
|
|
536
|
+
styles={{
|
|
537
|
+
"--list-marker-color": "blue",
|
|
538
|
+
"--list-marker-content": "'✓'",
|
|
539
|
+
} as React.CSSProperties}
|
|
540
|
+
>
|
|
541
|
+
<List.ListItem>Feature 1</List.ListItem>
|
|
542
|
+
<List.ListItem>Feature 2</List.ListItem>
|
|
543
|
+
</List>
|
|
544
|
+
);
|
|
545
|
+
|
|
546
|
+
const list = screen.getByRole("list");
|
|
547
|
+
expect(list).toHaveAttribute("data-variant", "custom");
|
|
548
|
+
expect(list).toHaveStyle({
|
|
549
|
+
"--list-marker-color": "blue",
|
|
550
|
+
"--list-marker-content": "'✓'",
|
|
551
|
+
});
|
|
552
|
+
});
|
|
553
|
+
});
|
|
554
|
+
});
|