@fpkit/acss 3.1.0 → 3.2.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-2NRIP6RB.cjs → chunk-2C3YLBWP.cjs} +3 -3
- package/libs/{chunk-NWJDAHP6.cjs → chunk-2GJHKWEK.cjs} +3 -3
- package/libs/{chunk-FVROL3V5.js → chunk-2JCDEC32.js} +3 -3
- package/libs/{chunk-IRLFZ3OL.js → chunk-3XJC4XUG.js} +2 -2
- package/libs/{chunk-23ANBDCR.js → chunk-4I5MF54P.js} +3 -3
- package/libs/chunk-4I5MF54P.js.map +1 -0
- package/libs/chunk-5CJPTDK3.cjs +31 -0
- package/libs/chunk-5CJPTDK3.cjs.map +1 -0
- package/libs/{chunk-E4OSROCA.cjs → chunk-5QSNJQVH.cjs} +3 -3
- package/libs/{chunk-O3JIHC5M.cjs → chunk-6BUJZ4DJ.cjs} +3 -3
- package/libs/{chunk-WXBFBWYF.cjs → chunk-AFINOD2L.cjs} +3 -3
- package/libs/{chunk-HRRHPLER.js → chunk-AWZLSWDO.js} +2 -2
- package/libs/chunk-DDSXKOUB.js +7 -0
- package/libs/chunk-DDSXKOUB.js.map +1 -0
- package/libs/{chunk-CWRNJA4P.js → chunk-DIJBIOFE.js} +3 -3
- package/libs/chunk-EJ6KYBFE.cjs +13 -0
- package/libs/chunk-EJ6KYBFE.cjs.map +1 -0
- package/libs/{chunk-GUJSMQ3V.cjs → chunk-EKJYOCLY.cjs} +3 -3
- package/libs/{chunk-X5RKCLDC.cjs → chunk-F64GE6RG.cjs} +4 -4
- package/libs/chunk-FMIM3332.js +8 -0
- package/libs/chunk-FMIM3332.js.map +1 -0
- package/libs/{chunk-5RAWNUVD.js → chunk-IBUTNPTQ.js} +2 -2
- package/libs/chunk-IWP4VJEP.cjs +18 -0
- package/libs/chunk-IWP4VJEP.cjs.map +1 -0
- package/libs/{chunk-ZFJ4U45S.js → chunk-KDMX3FAW.js} +2 -2
- package/libs/{chunk-DYFAUAB7.cjs → chunk-LXODKKA3.cjs} +4 -4
- package/libs/chunk-M7JLT62Q.js +9 -0
- package/libs/chunk-M7JLT62Q.js.map +1 -0
- package/libs/{chunk-IQ76HGVP.js → chunk-MBWI67UT.js} +2 -2
- package/libs/{chunk-O5XAJ7BY.cjs → chunk-NCGVF2QS.cjs} +4 -4
- package/libs/{chunk-W2UIN7EV.cjs → chunk-NPWHQVYB.cjs} +3 -3
- package/libs/{chunk-G55UJ53G.cjs → chunk-NZVSXRTB.cjs} +3 -3
- package/libs/chunk-NZVSXRTB.cjs.map +1 -0
- package/libs/{chunk-43TK2ICH.js → chunk-PMWL5XZ4.js} +3 -3
- package/libs/{chunk-KVKQLRJG.js → chunk-TF3GQKOY.js} +2 -2
- package/libs/chunk-TNEJXNZA.cjs +22 -0
- package/libs/chunk-TNEJXNZA.cjs.map +1 -0
- package/libs/{chunk-IEB64SWY.js → chunk-U5VA34SU.js} +2 -2
- package/libs/chunk-UGMP72J2.js +8 -0
- package/libs/chunk-UGMP72J2.js.map +1 -0
- package/libs/{chunk-MGPWZRBX.cjs → chunk-URBGDUFN.cjs} +6 -6
- package/libs/{chunk-QKHPHMG2.js → chunk-ZF6Y7W57.js} +5 -5
- package/libs/component-props-50e69975.d.ts +66 -0
- package/libs/components/box/box.css +1 -0
- package/libs/components/box/box.css.map +1 -0
- package/libs/components/box/box.min.css +3 -0
- package/libs/components/breadcrumbs/breadcrumb.cjs +6 -6
- package/libs/components/breadcrumbs/breadcrumb.js +3 -3
- package/libs/components/button.cjs +4 -4
- package/libs/components/button.d.cts +10 -3
- package/libs/components/button.d.ts +10 -3
- package/libs/components/button.js +2 -2
- package/libs/components/card.cjs +7 -7
- package/libs/components/card.d.cts +13 -85
- package/libs/components/card.d.ts +13 -85
- package/libs/components/card.js +2 -2
- package/libs/components/cards/card.css +1 -1
- package/libs/components/cards/card.css.map +1 -1
- package/libs/components/cards/card.min.css +2 -2
- package/libs/components/cluster/cluster.css +1 -0
- package/libs/components/cluster/cluster.css.map +1 -0
- package/libs/components/cluster/cluster.min.css +3 -0
- package/libs/components/dialog/dialog.cjs +7 -7
- package/libs/components/dialog/dialog.js +5 -5
- package/libs/components/form/fields.cjs +4 -4
- package/libs/components/form/fields.js +2 -2
- package/libs/components/form/textarea.cjs +4 -4
- package/libs/components/form/textarea.js +2 -2
- package/libs/components/grid/grid.css +1 -0
- package/libs/components/grid/grid.css.map +1 -0
- package/libs/components/grid/grid.min.css +3 -0
- package/libs/components/heading/heading.cjs +3 -3
- package/libs/components/heading/heading.js +2 -2
- package/libs/components/icons/icon.cjs +4 -4
- package/libs/components/icons/icon.d.cts +2 -2
- package/libs/components/icons/icon.d.ts +2 -2
- package/libs/components/icons/icon.js +2 -2
- package/libs/components/link/link.cjs +6 -6
- package/libs/components/link/link.js +2 -2
- package/libs/components/list/list.cjs +5 -5
- package/libs/components/list/list.js +2 -2
- package/libs/components/modal.cjs +4 -4
- package/libs/components/modal.d.cts +1 -1
- package/libs/components/modal.d.ts +1 -1
- package/libs/components/modal.js +3 -3
- package/libs/components/nav/nav.cjs +7 -7
- package/libs/components/nav/nav.js +3 -3
- package/libs/components/popover/popover.cjs +4 -4
- package/libs/components/popover/popover.d.cts +1 -1
- package/libs/components/popover/popover.d.ts +1 -1
- package/libs/components/popover/popover.js +1 -1
- package/libs/components/stack/stack.css +1 -0
- package/libs/components/stack/stack.css.map +1 -0
- package/libs/components/stack/stack.min.css +3 -0
- package/libs/components/tables/table.cjs +4 -4
- package/libs/components/tables/table.d.cts +2 -2
- package/libs/components/tables/table.d.ts +2 -2
- package/libs/components/tables/table.js +1 -1
- package/libs/components/text/text.cjs +5 -5
- package/libs/components/text/text.js +2 -2
- package/libs/hooks.cjs +4 -4
- package/libs/hooks.js +3 -3
- package/libs/{icons-287fce3a.d.ts → icons-df8e744f.d.ts} +1 -1
- 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 +74 -73
- 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 +925 -6
- package/libs/index.d.ts +925 -6
- package/libs/index.js +30 -30
- package/libs/index.js.map +1 -1
- package/package.json +2 -2
- package/src/App.tsx +1 -3
- package/src/components/alert/STYLES.mdx +790 -0
- package/src/components/badge/STYLES.mdx +610 -0
- package/src/components/box/README.mdx +401 -0
- package/src/components/box/STYLES.mdx +360 -0
- package/src/components/box/box.scss +245 -0
- package/src/components/box/box.stories.tsx +395 -0
- package/src/components/box/box.test.tsx +425 -0
- package/src/components/box/box.tsx +170 -0
- package/src/components/box/box.types.ts +166 -0
- package/src/components/breadcrumbs/STYLES.mdx +99 -0
- package/src/components/breadcrumbs/bc-item.tsx +0 -1
- package/src/components/buttons/STYLES.mdx +766 -0
- package/src/components/cards/STYLES.mdx +835 -0
- package/src/components/cards/card.scss +29 -21
- package/src/components/cards/card.tsx +13 -3
- package/src/components/cards/card.types.ts +13 -0
- package/src/components/cluster/README.mdx +595 -0
- package/src/components/cluster/STYLES.mdx +626 -0
- package/src/components/cluster/cluster.scss +86 -0
- package/src/components/cluster/cluster.stories.tsx +385 -0
- package/src/components/cluster/cluster.test.tsx +655 -0
- package/src/components/cluster/cluster.tsx +94 -0
- package/src/components/cluster/cluster.types.ts +75 -0
- package/src/components/details/STYLES.mdx +445 -0
- package/src/components/dialog/STYLES.mdx +888 -0
- package/src/components/flexbox/STYLES.mdx +857 -0
- package/src/components/flexbox/flex.stories.tsx +842 -141
- package/src/components/flexbox/flex.types.ts +25 -6
- package/src/components/form/STYLES.mdx +821 -0
- package/src/components/grid/README.mdx +709 -0
- package/src/components/grid/STYLES.mdx +785 -0
- package/src/components/grid/grid.scss +287 -0
- package/src/components/grid/grid.stories.tsx +486 -0
- package/src/components/grid/grid.test.tsx +981 -0
- package/src/components/grid/grid.tsx +222 -0
- package/src/components/grid/grid.types.ts +344 -0
- package/src/components/icons/STYLES.mdx +56 -0
- package/src/components/icons/components/arrow-right.tsx +0 -5
- package/src/components/images/STYLES.mdx +75 -0
- package/src/components/kit.tsx +8 -4
- package/src/components/layout/STYLES.mdx +556 -0
- package/src/components/link/STYLES.mdx +75 -0
- package/src/components/list/STYLES.mdx +631 -0
- package/src/components/nav/STYLES.mdx +460 -0
- package/src/components/popover/popover.tsx +1 -1
- package/src/components/progress/STYLES.mdx +64 -0
- package/src/components/stack/README.mdx +400 -0
- package/src/components/stack/STYLES.mdx +414 -0
- package/src/components/stack/stack.scss +109 -0
- package/src/components/stack/stack.stories.tsx +559 -0
- package/src/components/stack/stack.test.tsx +426 -0
- package/src/components/stack/stack.tsx +141 -0
- package/src/components/stack/stack.types.ts +133 -0
- package/src/components/tables/table-elements.tsx +1 -1
- package/src/components/tables/table.tsx +2 -2
- package/src/components/tag/STYLES.mdx +105 -0
- package/src/components/text-to-speech/STYLES.mdx +80 -0
- package/src/components/text-to-speech/TextToSpeech.tsx +0 -4
- package/src/components/text-to-speech/useTextToSpeech.tsx +2 -6
- package/src/components/ui.tsx +3 -3
- package/src/decorators/instructions.tsx +22 -18
- package/src/hooks/popover/popover.tsx +1 -1
- package/src/index.scss +5 -1
- package/src/index.ts +305 -12
- package/src/sass/GLOBALS-STYLES.md +631 -0
- package/src/sass/_globals.scss +45 -24
- package/src/styles/box/box.css +220 -0
- package/src/styles/box/box.css.map +1 -0
- package/src/styles/cards/card.css +22 -17
- package/src/styles/cards/card.css.map +1 -1
- package/src/styles/cluster/cluster.css +71 -0
- package/src/styles/cluster/cluster.css.map +1 -0
- package/src/styles/grid/grid.css +238 -0
- package/src/styles/grid/grid.css.map +1 -0
- package/src/styles/index.css +667 -49
- package/src/styles/index.css.map +1 -1
- package/src/styles/stack/stack.css +86 -0
- package/src/styles/stack/stack.css.map +1 -0
- package/src/types/component-props.ts +42 -13
- package/src/types/layout-primitives.ts +48 -0
- package/src/types/shared.ts +10 -26
- package/libs/chunk-23ANBDCR.js.map +0 -1
- package/libs/chunk-5QD3DWFI.js +0 -9
- package/libs/chunk-5QD3DWFI.js.map +0 -1
- package/libs/chunk-6WTC4JXH.cjs +0 -31
- package/libs/chunk-6WTC4JXH.cjs.map +0 -1
- package/libs/chunk-ENTCUJ3A.cjs +0 -13
- package/libs/chunk-ENTCUJ3A.cjs.map +0 -1
- package/libs/chunk-G55UJ53G.cjs.map +0 -1
- package/libs/chunk-HHLNOC5T.js +0 -7
- package/libs/chunk-HHLNOC5T.js.map +0 -1
- package/libs/chunk-KK47SYZI.js +0 -8
- package/libs/chunk-KK47SYZI.js.map +0 -1
- package/libs/chunk-US2I5GI7.cjs +0 -22
- package/libs/chunk-US2I5GI7.cjs.map +0 -1
- package/libs/chunk-W5TKWBFC.cjs +0 -18
- package/libs/chunk-W5TKWBFC.cjs.map +0 -1
- package/libs/chunk-Y2PFDELK.js +0 -8
- package/libs/chunk-Y2PFDELK.js.map +0 -1
- package/libs/component-props-67d978a2.d.ts +0 -38
- /package/libs/{chunk-2NRIP6RB.cjs.map → chunk-2C3YLBWP.cjs.map} +0 -0
- /package/libs/{chunk-NWJDAHP6.cjs.map → chunk-2GJHKWEK.cjs.map} +0 -0
- /package/libs/{chunk-FVROL3V5.js.map → chunk-2JCDEC32.js.map} +0 -0
- /package/libs/{chunk-IRLFZ3OL.js.map → chunk-3XJC4XUG.js.map} +0 -0
- /package/libs/{chunk-E4OSROCA.cjs.map → chunk-5QSNJQVH.cjs.map} +0 -0
- /package/libs/{chunk-O3JIHC5M.cjs.map → chunk-6BUJZ4DJ.cjs.map} +0 -0
- /package/libs/{chunk-WXBFBWYF.cjs.map → chunk-AFINOD2L.cjs.map} +0 -0
- /package/libs/{chunk-HRRHPLER.js.map → chunk-AWZLSWDO.js.map} +0 -0
- /package/libs/{chunk-CWRNJA4P.js.map → chunk-DIJBIOFE.js.map} +0 -0
- /package/libs/{chunk-GUJSMQ3V.cjs.map → chunk-EKJYOCLY.cjs.map} +0 -0
- /package/libs/{chunk-X5RKCLDC.cjs.map → chunk-F64GE6RG.cjs.map} +0 -0
- /package/libs/{chunk-5RAWNUVD.js.map → chunk-IBUTNPTQ.js.map} +0 -0
- /package/libs/{chunk-ZFJ4U45S.js.map → chunk-KDMX3FAW.js.map} +0 -0
- /package/libs/{chunk-DYFAUAB7.cjs.map → chunk-LXODKKA3.cjs.map} +0 -0
- /package/libs/{chunk-IQ76HGVP.js.map → chunk-MBWI67UT.js.map} +0 -0
- /package/libs/{chunk-O5XAJ7BY.cjs.map → chunk-NCGVF2QS.cjs.map} +0 -0
- /package/libs/{chunk-W2UIN7EV.cjs.map → chunk-NPWHQVYB.cjs.map} +0 -0
- /package/libs/{chunk-43TK2ICH.js.map → chunk-PMWL5XZ4.js.map} +0 -0
- /package/libs/{chunk-KVKQLRJG.js.map → chunk-TF3GQKOY.js.map} +0 -0
- /package/libs/{chunk-IEB64SWY.js.map → chunk-U5VA34SU.js.map} +0 -0
- /package/libs/{chunk-MGPWZRBX.cjs.map → chunk-URBGDUFN.cjs.map} +0 -0
- /package/libs/{chunk-QKHPHMG2.js.map → chunk-ZF6Y7W57.js.map} +0 -0
|
@@ -0,0 +1,425 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { render, screen } from "@testing-library/react";
|
|
3
|
+
import { Box } from "./box";
|
|
4
|
+
|
|
5
|
+
describe("Box", () => {
|
|
6
|
+
// ============================================================================
|
|
7
|
+
// Rendering Tests
|
|
8
|
+
// ============================================================================
|
|
9
|
+
|
|
10
|
+
it("renders with default props", () => {
|
|
11
|
+
render(<Box>Content</Box>);
|
|
12
|
+
expect(screen.getByText("Content")).toBeInTheDocument();
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
it("renders children correctly", () => {
|
|
16
|
+
render(
|
|
17
|
+
<Box>
|
|
18
|
+
<span>Child 1</span>
|
|
19
|
+
<span>Child 2</span>
|
|
20
|
+
</Box>
|
|
21
|
+
);
|
|
22
|
+
expect(screen.getByText("Child 1")).toBeInTheDocument();
|
|
23
|
+
expect(screen.getByText("Child 2")).toBeInTheDocument();
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it("renders as div by default", () => {
|
|
27
|
+
render(<Box data-testid="box">Content</Box>);
|
|
28
|
+
const box = screen.getByTestId("box");
|
|
29
|
+
expect(box.tagName).toBe("DIV");
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
// ============================================================================
|
|
33
|
+
// Polymorphic Rendering Tests
|
|
34
|
+
// ============================================================================
|
|
35
|
+
|
|
36
|
+
it("renders as different elements via as prop", () => {
|
|
37
|
+
render(<Box as="section" data-testid="box">Content</Box>);
|
|
38
|
+
const box = screen.getByTestId("box");
|
|
39
|
+
expect(box.tagName).toBe("SECTION");
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it("renders as article", () => {
|
|
43
|
+
render(<Box as="article" data-testid="box">Content</Box>);
|
|
44
|
+
const box = screen.getByTestId("box");
|
|
45
|
+
expect(box.tagName).toBe("ARTICLE");
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it("renders as main", () => {
|
|
49
|
+
render(<Box as="main" data-testid="box">Content</Box>);
|
|
50
|
+
const box = screen.getByTestId("box");
|
|
51
|
+
expect(box.tagName).toBe("MAIN");
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it("renders as header", () => {
|
|
55
|
+
render(<Box as="header" data-testid="box">Content</Box>);
|
|
56
|
+
const box = screen.getByTestId("box");
|
|
57
|
+
expect(box.tagName).toBe("HEADER");
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it("renders as footer", () => {
|
|
61
|
+
render(<Box as="footer" data-testid="box">Content</Box>);
|
|
62
|
+
const box = screen.getByTestId("box");
|
|
63
|
+
expect(box.tagName).toBe("FOOTER");
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
// ============================================================================
|
|
67
|
+
// Padding Class Tests
|
|
68
|
+
// ============================================================================
|
|
69
|
+
|
|
70
|
+
it("applies padding class", () => {
|
|
71
|
+
render(<Box padding="md" data-testid="box">Content</Box>);
|
|
72
|
+
const box = screen.getByTestId("box");
|
|
73
|
+
expect(box).toHaveClass("box-padding-md");
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it("applies paddingInline class", () => {
|
|
77
|
+
render(<Box paddingInline="lg" data-testid="box">Content</Box>);
|
|
78
|
+
const box = screen.getByTestId("box");
|
|
79
|
+
expect(box).toHaveClass("box-padding-inline-lg");
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
it("applies paddingBlock class", () => {
|
|
83
|
+
render(<Box paddingBlock="sm" data-testid="box">Content</Box>);
|
|
84
|
+
const box = screen.getByTestId("box");
|
|
85
|
+
expect(box).toHaveClass("box-padding-block-sm");
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it("applies multiple padding classes", () => {
|
|
89
|
+
render(
|
|
90
|
+
<Box padding="md" paddingInline="lg" paddingBlock="sm" data-testid="box">
|
|
91
|
+
Content
|
|
92
|
+
</Box>
|
|
93
|
+
);
|
|
94
|
+
const box = screen.getByTestId("box");
|
|
95
|
+
expect(box).toHaveClass("box-padding-md");
|
|
96
|
+
expect(box).toHaveClass("box-padding-inline-lg");
|
|
97
|
+
expect(box).toHaveClass("box-padding-block-sm");
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
it("applies padding-0 class", () => {
|
|
101
|
+
render(<Box padding="0" data-testid="box">Content</Box>);
|
|
102
|
+
const box = screen.getByTestId("box");
|
|
103
|
+
expect(box).toHaveClass("box-padding-0");
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
// ============================================================================
|
|
107
|
+
// Margin Class Tests
|
|
108
|
+
// ============================================================================
|
|
109
|
+
|
|
110
|
+
it("applies margin class", () => {
|
|
111
|
+
render(<Box margin="lg" data-testid="box">Content</Box>);
|
|
112
|
+
const box = screen.getByTestId("box");
|
|
113
|
+
expect(box).toHaveClass("box-margin-lg");
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
it("applies marginInline class", () => {
|
|
117
|
+
render(<Box marginInline="xl" data-testid="box">Content</Box>);
|
|
118
|
+
const box = screen.getByTestId("box");
|
|
119
|
+
expect(box).toHaveClass("box-margin-inline-xl");
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
it("applies marginBlock class", () => {
|
|
123
|
+
render(<Box marginBlock="md" data-testid="box">Content</Box>);
|
|
124
|
+
const box = screen.getByTestId("box");
|
|
125
|
+
expect(box).toHaveClass("box-margin-block-md");
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
it("applies margin-0 class", () => {
|
|
129
|
+
render(<Box margin="0" data-testid="box">Content</Box>);
|
|
130
|
+
const box = screen.getByTestId("box");
|
|
131
|
+
expect(box).toHaveClass("box-margin-0");
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
// ============================================================================
|
|
135
|
+
// Width Class Tests
|
|
136
|
+
// ============================================================================
|
|
137
|
+
|
|
138
|
+
it("applies width auto class", () => {
|
|
139
|
+
render(<Box width="auto" data-testid="box">Content</Box>);
|
|
140
|
+
const box = screen.getByTestId("box");
|
|
141
|
+
expect(box).toHaveClass("box-width-auto");
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
it("applies width full class", () => {
|
|
145
|
+
render(<Box width="full" data-testid="box">Content</Box>);
|
|
146
|
+
const box = screen.getByTestId("box");
|
|
147
|
+
expect(box).toHaveClass("box-width-full");
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
it("applies width fit class", () => {
|
|
151
|
+
render(<Box width="fit" data-testid="box">Content</Box>);
|
|
152
|
+
const box = screen.getByTestId("box");
|
|
153
|
+
expect(box).toHaveClass("box-width-fit");
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
it("applies width max class", () => {
|
|
157
|
+
render(<Box width="max" data-testid="box">Content</Box>);
|
|
158
|
+
const box = screen.getByTestId("box");
|
|
159
|
+
expect(box).toHaveClass("box-width-max");
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
// ============================================================================
|
|
163
|
+
// Max-Width Class Tests
|
|
164
|
+
// ============================================================================
|
|
165
|
+
|
|
166
|
+
it("applies maxWidth xs class", () => {
|
|
167
|
+
render(<Box maxWidth="xs" data-testid="box">Content</Box>);
|
|
168
|
+
const box = screen.getByTestId("box");
|
|
169
|
+
expect(box).toHaveClass("box-max-width-xs");
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
it("applies maxWidth sm class", () => {
|
|
173
|
+
render(<Box maxWidth="sm" data-testid="box">Content</Box>);
|
|
174
|
+
const box = screen.getByTestId("box");
|
|
175
|
+
expect(box).toHaveClass("box-max-width-sm");
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
it("applies maxWidth md class", () => {
|
|
179
|
+
render(<Box maxWidth="md" data-testid="box">Content</Box>);
|
|
180
|
+
const box = screen.getByTestId("box");
|
|
181
|
+
expect(box).toHaveClass("box-max-width-md");
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
it("applies maxWidth lg class", () => {
|
|
185
|
+
render(<Box maxWidth="lg" data-testid="box">Content</Box>);
|
|
186
|
+
const box = screen.getByTestId("box");
|
|
187
|
+
expect(box).toHaveClass("box-max-width-lg");
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
it("applies maxWidth xl class", () => {
|
|
191
|
+
render(<Box maxWidth="xl" data-testid="box">Content</Box>);
|
|
192
|
+
const box = screen.getByTestId("box");
|
|
193
|
+
expect(box).toHaveClass("box-max-width-xl");
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
it("applies maxWidth container class", () => {
|
|
197
|
+
render(<Box maxWidth="container" data-testid="box">Content</Box>);
|
|
198
|
+
const box = screen.getByTestId("box");
|
|
199
|
+
expect(box).toHaveClass("box-max-width-container");
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
// ============================================================================
|
|
203
|
+
// Border Radius Class Tests
|
|
204
|
+
// ============================================================================
|
|
205
|
+
|
|
206
|
+
it("applies radius xs class", () => {
|
|
207
|
+
render(<Box radius="xs" data-testid="box">Content</Box>);
|
|
208
|
+
const box = screen.getByTestId("box");
|
|
209
|
+
expect(box).toHaveClass("box-radius-xs");
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
it("applies radius sm class", () => {
|
|
213
|
+
render(<Box radius="sm" data-testid="box">Content</Box>);
|
|
214
|
+
const box = screen.getByTestId("box");
|
|
215
|
+
expect(box).toHaveClass("box-radius-sm");
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
it("applies radius md class", () => {
|
|
219
|
+
render(<Box radius="md" data-testid="box">Content</Box>);
|
|
220
|
+
const box = screen.getByTestId("box");
|
|
221
|
+
expect(box).toHaveClass("box-radius-md");
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
it("applies radius lg class", () => {
|
|
225
|
+
render(<Box radius="lg" data-testid="box">Content</Box>);
|
|
226
|
+
const box = screen.getByTestId("box");
|
|
227
|
+
expect(box).toHaveClass("box-radius-lg");
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
it("applies radius xl class", () => {
|
|
231
|
+
render(<Box radius="xl" data-testid="box">Content</Box>);
|
|
232
|
+
const box = screen.getByTestId("box");
|
|
233
|
+
expect(box).toHaveClass("box-radius-xl");
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
it("applies radius full class", () => {
|
|
237
|
+
render(<Box radius="full" data-testid="box">Content</Box>);
|
|
238
|
+
const box = screen.getByTestId("box");
|
|
239
|
+
expect(box).toHaveClass("box-radius-full");
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
it("applies radius 0 class", () => {
|
|
243
|
+
render(<Box radius="0" data-testid="box">Content</Box>);
|
|
244
|
+
const box = screen.getByTestId("box");
|
|
245
|
+
expect(box).toHaveClass("box-radius-0");
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
// ============================================================================
|
|
249
|
+
// Multiple Props Tests
|
|
250
|
+
// ============================================================================
|
|
251
|
+
|
|
252
|
+
it("applies multiple utility classes", () => {
|
|
253
|
+
render(
|
|
254
|
+
<Box
|
|
255
|
+
padding="md"
|
|
256
|
+
margin="lg"
|
|
257
|
+
width="full"
|
|
258
|
+
maxWidth="container"
|
|
259
|
+
radius="md"
|
|
260
|
+
data-testid="box"
|
|
261
|
+
>
|
|
262
|
+
Content
|
|
263
|
+
</Box>
|
|
264
|
+
);
|
|
265
|
+
const box = screen.getByTestId("box");
|
|
266
|
+
expect(box).toHaveClass("box-padding-md");
|
|
267
|
+
expect(box).toHaveClass("box-margin-lg");
|
|
268
|
+
expect(box).toHaveClass("box-width-full");
|
|
269
|
+
expect(box).toHaveClass("box-max-width-container");
|
|
270
|
+
expect(box).toHaveClass("box-radius-md");
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
// ============================================================================
|
|
274
|
+
// Custom Class Tests
|
|
275
|
+
// ============================================================================
|
|
276
|
+
|
|
277
|
+
it("merges custom className with utility classes", () => {
|
|
278
|
+
render(
|
|
279
|
+
<Box padding="md" className="custom-class" data-testid="box">
|
|
280
|
+
Content
|
|
281
|
+
</Box>
|
|
282
|
+
);
|
|
283
|
+
const box = screen.getByTestId("box");
|
|
284
|
+
expect(box).toHaveClass("box-padding-md");
|
|
285
|
+
expect(box).toHaveClass("custom-class");
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
it("merges custom classes with utility classes", () => {
|
|
289
|
+
render(
|
|
290
|
+
<Box padding="md" classes="legacy-class" data-testid="box">
|
|
291
|
+
Content
|
|
292
|
+
</Box>
|
|
293
|
+
);
|
|
294
|
+
const box = screen.getByTestId("box");
|
|
295
|
+
expect(box).toHaveClass("box-padding-md");
|
|
296
|
+
expect(box).toHaveClass("legacy-class");
|
|
297
|
+
});
|
|
298
|
+
|
|
299
|
+
it("merges both className and classes with utility classes", () => {
|
|
300
|
+
render(
|
|
301
|
+
<Box
|
|
302
|
+
padding="md"
|
|
303
|
+
className="custom-class"
|
|
304
|
+
classes="legacy-class"
|
|
305
|
+
data-testid="box"
|
|
306
|
+
>
|
|
307
|
+
Content
|
|
308
|
+
</Box>
|
|
309
|
+
);
|
|
310
|
+
const box = screen.getByTestId("box");
|
|
311
|
+
expect(box).toHaveClass("box-padding-md");
|
|
312
|
+
expect(box).toHaveClass("custom-class");
|
|
313
|
+
expect(box).toHaveClass("legacy-class");
|
|
314
|
+
});
|
|
315
|
+
|
|
316
|
+
// ============================================================================
|
|
317
|
+
// Inline Styles Tests
|
|
318
|
+
// ============================================================================
|
|
319
|
+
|
|
320
|
+
it("applies custom styles", () => {
|
|
321
|
+
render(
|
|
322
|
+
<Box
|
|
323
|
+
padding="md"
|
|
324
|
+
styles={{ backgroundColor: "red", color: "white" }}
|
|
325
|
+
data-testid="box"
|
|
326
|
+
>
|
|
327
|
+
Content
|
|
328
|
+
</Box>
|
|
329
|
+
);
|
|
330
|
+
const box = screen.getByTestId("box");
|
|
331
|
+
expect(box).toHaveStyle({ backgroundColor: "rgb(255, 0, 0)" });
|
|
332
|
+
expect(box).toHaveStyle({ color: "rgb(255, 255, 255)" });
|
|
333
|
+
});
|
|
334
|
+
|
|
335
|
+
it("accepts CSS custom properties in styles", () => {
|
|
336
|
+
render(
|
|
337
|
+
<Box
|
|
338
|
+
padding="md"
|
|
339
|
+
styles={{ "--spacing-md": "2rem" } as React.CSSProperties}
|
|
340
|
+
data-testid="box"
|
|
341
|
+
>
|
|
342
|
+
Content
|
|
343
|
+
</Box>
|
|
344
|
+
);
|
|
345
|
+
const box = screen.getByTestId("box");
|
|
346
|
+
expect(box).toHaveAttribute("style");
|
|
347
|
+
});
|
|
348
|
+
|
|
349
|
+
// ============================================================================
|
|
350
|
+
// Ref Forwarding Tests
|
|
351
|
+
// ============================================================================
|
|
352
|
+
|
|
353
|
+
it("forwards ref correctly", () => {
|
|
354
|
+
const ref = React.createRef<HTMLDivElement>();
|
|
355
|
+
render(
|
|
356
|
+
<Box ref={ref} data-testid="box">
|
|
357
|
+
Content
|
|
358
|
+
</Box>
|
|
359
|
+
);
|
|
360
|
+
expect(ref.current).toBeInstanceOf(HTMLDivElement);
|
|
361
|
+
});
|
|
362
|
+
|
|
363
|
+
it("forwards ref with polymorphic as prop", () => {
|
|
364
|
+
const ref = React.createRef<HTMLElement>();
|
|
365
|
+
render(
|
|
366
|
+
<Box as="section" ref={ref} data-testid="box">
|
|
367
|
+
Content
|
|
368
|
+
</Box>
|
|
369
|
+
);
|
|
370
|
+
expect(ref.current).toBeInstanceOf(HTMLElement);
|
|
371
|
+
expect(ref.current?.tagName).toBe("SECTION");
|
|
372
|
+
});
|
|
373
|
+
|
|
374
|
+
// ============================================================================
|
|
375
|
+
// Accessibility Tests
|
|
376
|
+
// ============================================================================
|
|
377
|
+
|
|
378
|
+
it("forwards ARIA attributes", () => {
|
|
379
|
+
render(
|
|
380
|
+
<Box aria-label="Test Box" role="region" data-testid="box">
|
|
381
|
+
Content
|
|
382
|
+
</Box>
|
|
383
|
+
);
|
|
384
|
+
const box = screen.getByTestId("box");
|
|
385
|
+
expect(box).toHaveAttribute("aria-label", "Test Box");
|
|
386
|
+
expect(box).toHaveAttribute("role", "region");
|
|
387
|
+
});
|
|
388
|
+
|
|
389
|
+
it("forwards data attributes", () => {
|
|
390
|
+
render(
|
|
391
|
+
<Box data-testid="box" data-custom="value">
|
|
392
|
+
Content
|
|
393
|
+
</Box>
|
|
394
|
+
);
|
|
395
|
+
const box = screen.getByTestId("box");
|
|
396
|
+
expect(box).toHaveAttribute("data-custom", "value");
|
|
397
|
+
});
|
|
398
|
+
|
|
399
|
+
// ============================================================================
|
|
400
|
+
// Edge Cases
|
|
401
|
+
// ============================================================================
|
|
402
|
+
|
|
403
|
+
it("handles empty children", () => {
|
|
404
|
+
render(<Box data-testid="box" />);
|
|
405
|
+
const box = screen.getByTestId("box");
|
|
406
|
+
expect(box).toBeInTheDocument();
|
|
407
|
+
expect(box).toBeEmptyDOMElement();
|
|
408
|
+
});
|
|
409
|
+
|
|
410
|
+
it("handles no props gracefully", () => {
|
|
411
|
+
render(<Box data-testid="box">Content</Box>);
|
|
412
|
+
const box = screen.getByTestId("box");
|
|
413
|
+
expect(box).toBeInTheDocument();
|
|
414
|
+
expect(box.tagName).toBe("DIV");
|
|
415
|
+
});
|
|
416
|
+
|
|
417
|
+
it("does not apply classes when no spacing props provided", () => {
|
|
418
|
+
render(<Box data-testid="box">Content</Box>);
|
|
419
|
+
const box = screen.getByTestId("box");
|
|
420
|
+
// Should not have any box-* utility classes
|
|
421
|
+
const classList = Array.from(box.classList);
|
|
422
|
+
const hasBoxUtilityClass = classList.some((cls) => cls.startsWith("box-"));
|
|
423
|
+
expect(hasBoxUtilityClass).toBe(false);
|
|
424
|
+
});
|
|
425
|
+
});
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import UI from "../ui";
|
|
3
|
+
import type { BoxProps } from "./box.types";
|
|
4
|
+
|
|
5
|
+
// Re-export types for convenience
|
|
6
|
+
export type { BoxProps } from "./box.types";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Box - A fundamental container primitive for spacing and sizing control.
|
|
10
|
+
*
|
|
11
|
+
* The Box component is the foundational layout primitive in fpkit, providing a flexible,
|
|
12
|
+
* semantic container with comprehensive control over spacing (padding/margin), sizing,
|
|
13
|
+
* and visual appearance. It uses utility classes generated from props, ensuring consistent
|
|
14
|
+
* styling across the application.
|
|
15
|
+
*
|
|
16
|
+
* ## Key Features
|
|
17
|
+
* - **Unified Spacing Scale**: Fluid responsive spacing using CSS clamp()
|
|
18
|
+
* - **Logical Properties**: `padding-inline`/`padding-block` for i18n support
|
|
19
|
+
* - **Polymorphic Rendering**: Render as any semantic HTML element via `as` prop
|
|
20
|
+
* - **CSS Custom Properties**: All values customizable for theming
|
|
21
|
+
* - **Type-Safe**: Full TypeScript support with IntelliSense
|
|
22
|
+
*
|
|
23
|
+
* ## Accessibility
|
|
24
|
+
* - Uses semantic HTML elements by default
|
|
25
|
+
* - Supports ARIA attributes via spread props
|
|
26
|
+
* - Encourages semantic elements via `as` prop
|
|
27
|
+
* - All props forwarded to underlying element
|
|
28
|
+
*
|
|
29
|
+
* ## Use Cases
|
|
30
|
+
* - Container with padding/margin
|
|
31
|
+
* - Centered layouts with max-width
|
|
32
|
+
* - Card-like components
|
|
33
|
+
* - Spacing between sections
|
|
34
|
+
* - Semantic landmarks
|
|
35
|
+
*
|
|
36
|
+
* @example
|
|
37
|
+
* // Basic container with padding
|
|
38
|
+
* <Box padding="md">
|
|
39
|
+
* <h1>Content</h1>
|
|
40
|
+
* </Box>
|
|
41
|
+
*
|
|
42
|
+
* @example
|
|
43
|
+
* // Centered container with max width
|
|
44
|
+
* <Box
|
|
45
|
+
* padding="lg"
|
|
46
|
+
* maxWidth="container"
|
|
47
|
+
* style={{ marginInline: 'auto' }}
|
|
48
|
+
* >
|
|
49
|
+
* <article>Centered content</article>
|
|
50
|
+
* </Box>
|
|
51
|
+
*
|
|
52
|
+
* @example
|
|
53
|
+
* // Card-like box with radius
|
|
54
|
+
* <Box
|
|
55
|
+
* padding="lg"
|
|
56
|
+
* radius="md"
|
|
57
|
+
* as="article"
|
|
58
|
+
* styles={{
|
|
59
|
+
* backgroundColor: '#fff',
|
|
60
|
+
* boxShadow: '0 1px 3px rgba(0,0,0,0.1)'
|
|
61
|
+
* }}
|
|
62
|
+
* >
|
|
63
|
+
* <h2>Card Title</h2>
|
|
64
|
+
* <p>Card content</p>
|
|
65
|
+
* </Box>
|
|
66
|
+
*
|
|
67
|
+
* @example
|
|
68
|
+
* // Asymmetric spacing with logical properties
|
|
69
|
+
* <Box
|
|
70
|
+
* paddingInline="xl"
|
|
71
|
+
* paddingBlock="md"
|
|
72
|
+
* as="section"
|
|
73
|
+
* >
|
|
74
|
+
* <p>Wide horizontal padding, narrow vertical</p>
|
|
75
|
+
* </Box>
|
|
76
|
+
*
|
|
77
|
+
* @example
|
|
78
|
+
* // Semantic section with spacing
|
|
79
|
+
* <Box as="section" padding="xl" margin="lg">
|
|
80
|
+
* <h2>Section Title</h2>
|
|
81
|
+
* <p>Section content...</p>
|
|
82
|
+
* </Box>
|
|
83
|
+
*
|
|
84
|
+
* @see {@link BoxProps} for complete props documentation
|
|
85
|
+
*/
|
|
86
|
+
export const Box = React.forwardRef<HTMLElement, BoxProps>(
|
|
87
|
+
(
|
|
88
|
+
{
|
|
89
|
+
padding,
|
|
90
|
+
paddingInline,
|
|
91
|
+
paddingBlock,
|
|
92
|
+
margin,
|
|
93
|
+
marginInline,
|
|
94
|
+
marginBlock,
|
|
95
|
+
width,
|
|
96
|
+
maxWidth,
|
|
97
|
+
radius,
|
|
98
|
+
as = "div",
|
|
99
|
+
className,
|
|
100
|
+
classes,
|
|
101
|
+
children,
|
|
102
|
+
...props
|
|
103
|
+
},
|
|
104
|
+
ref
|
|
105
|
+
) => {
|
|
106
|
+
// Build utility classes array based on props
|
|
107
|
+
const utilityClasses: string[] = [];
|
|
108
|
+
|
|
109
|
+
// Padding utilities
|
|
110
|
+
if (padding) {
|
|
111
|
+
utilityClasses.push(`box-padding-${padding}`);
|
|
112
|
+
}
|
|
113
|
+
if (paddingInline) {
|
|
114
|
+
utilityClasses.push(`box-padding-inline-${paddingInline}`);
|
|
115
|
+
}
|
|
116
|
+
if (paddingBlock) {
|
|
117
|
+
utilityClasses.push(`box-padding-block-${paddingBlock}`);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Margin utilities
|
|
121
|
+
if (margin) {
|
|
122
|
+
utilityClasses.push(`box-margin-${margin}`);
|
|
123
|
+
}
|
|
124
|
+
if (marginInline) {
|
|
125
|
+
utilityClasses.push(`box-margin-inline-${marginInline}`);
|
|
126
|
+
}
|
|
127
|
+
if (marginBlock) {
|
|
128
|
+
utilityClasses.push(`box-margin-block-${marginBlock}`);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Width utilities
|
|
132
|
+
if (width) {
|
|
133
|
+
utilityClasses.push(`box-width-${width}`);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Max-width utilities
|
|
137
|
+
if (maxWidth) {
|
|
138
|
+
utilityClasses.push(`box-max-width-${maxWidth}`);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Border radius utilities
|
|
142
|
+
if (radius) {
|
|
143
|
+
utilityClasses.push(`box-radius-${radius}`);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// Merge all classes: utility classes, className prop, and classes prop
|
|
147
|
+
const allClasses = [
|
|
148
|
+
...utilityClasses,
|
|
149
|
+
className,
|
|
150
|
+
classes,
|
|
151
|
+
]
|
|
152
|
+
.filter(Boolean)
|
|
153
|
+
.join(" ");
|
|
154
|
+
|
|
155
|
+
return (
|
|
156
|
+
<UI
|
|
157
|
+
as={as}
|
|
158
|
+
ref={ref}
|
|
159
|
+
classes={allClasses || undefined}
|
|
160
|
+
{...props}
|
|
161
|
+
>
|
|
162
|
+
{children}
|
|
163
|
+
</UI>
|
|
164
|
+
);
|
|
165
|
+
}
|
|
166
|
+
);
|
|
167
|
+
|
|
168
|
+
Box.displayName = "Box";
|
|
169
|
+
|
|
170
|
+
export default Box;
|