@pyreon/coolgrid 0.11.1 → 0.11.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.
- package/package.json +9 -8
- package/src/Col/component.tsx +61 -0
- package/src/Col/index.ts +3 -0
- package/src/Col/styled.ts +107 -0
- package/src/Container/component.tsx +82 -0
- package/src/Container/index.ts +3 -0
- package/src/Container/styled.ts +37 -0
- package/src/Container/utils.ts +13 -0
- package/src/Row/component.tsx +79 -0
- package/src/Row/index.ts +3 -0
- package/src/Row/styled.ts +70 -0
- package/src/__tests__/Col.test.ts +131 -0
- package/src/__tests__/Container.styled.test.ts +49 -0
- package/src/__tests__/Container.test.ts +147 -0
- package/src/__tests__/Row.test.ts +135 -0
- package/src/__tests__/config.test.ts +120 -0
- package/src/__tests__/contextCascading.test.ts +114 -0
- package/src/__tests__/index.test.ts +35 -0
- package/src/__tests__/useContext.test.ts +92 -0
- package/src/__tests__/utils.test.ts +144 -0
- package/src/constants.ts +20 -0
- package/src/context/ContainerContext.ts +9 -0
- package/src/context/RowContext.ts +9 -0
- package/src/context/index.ts +4 -0
- package/src/index.ts +7 -0
- package/src/theme.ts +40 -0
- package/src/types.ts +72 -0
- package/src/useContext.tsx +54 -0
- package/src/utils.ts +23 -0
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import type { VNode } from "@pyreon/core"
|
|
2
|
+
import { beforeEach, describe, expect, it, vi } from "vitest"
|
|
3
|
+
|
|
4
|
+
const mockProvide = vi.fn()
|
|
5
|
+
const mockUseContext = vi.fn()
|
|
6
|
+
|
|
7
|
+
vi.mock("@pyreon/core", async (importOriginal) => {
|
|
8
|
+
const original = await importOriginal<typeof import("@pyreon/core")>()
|
|
9
|
+
return {
|
|
10
|
+
...original,
|
|
11
|
+
provide: (...args: any[]) => {
|
|
12
|
+
mockProvide(...args)
|
|
13
|
+
},
|
|
14
|
+
useContext: (ctx: any) => {
|
|
15
|
+
if (mockUseContext.mock.calls.length > 0) {
|
|
16
|
+
return mockUseContext(ctx)
|
|
17
|
+
}
|
|
18
|
+
return original.useContext(ctx)
|
|
19
|
+
},
|
|
20
|
+
}
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
const asVNode = (v: unknown) => v as VNode
|
|
24
|
+
|
|
25
|
+
describe("Container", () => {
|
|
26
|
+
beforeEach(() => {
|
|
27
|
+
vi.clearAllMocks()
|
|
28
|
+
// unistyle context returns empty theme
|
|
29
|
+
mockUseContext.mockReturnValue({ theme: {} })
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
it("returns a VNode", async () => {
|
|
33
|
+
const Container = (await import("../Container")).default
|
|
34
|
+
const result = asVNode(Container({ children: "test" }))
|
|
35
|
+
expect(result).toBeDefined()
|
|
36
|
+
expect(result.type).toBeDefined()
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
it("has correct displayName", async () => {
|
|
40
|
+
const Container = (await import("../Container")).default
|
|
41
|
+
expect(Container.displayName).toBe("@pyreon/coolgrid/Container")
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
it("has correct pkgName", async () => {
|
|
45
|
+
const Container = (await import("../Container")).default
|
|
46
|
+
expect(Container.pkgName).toBe("@pyreon/coolgrid")
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
it("has PYREON__COMPONENT static", async () => {
|
|
50
|
+
const Container = (await import("../Container")).default
|
|
51
|
+
expect(Container.PYREON__COMPONENT).toBe("@pyreon/coolgrid/Container")
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
it("passes $coolgrid prop with width and extraStyles", async () => {
|
|
55
|
+
const Container = (await import("../Container")).default
|
|
56
|
+
const result = asVNode(Container({ children: "test" }))
|
|
57
|
+
expect(result.props).toHaveProperty("$coolgrid")
|
|
58
|
+
expect(result.props.$coolgrid).toHaveProperty("width")
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
it("provides ContainerContext", async () => {
|
|
62
|
+
const Container = (await import("../Container")).default
|
|
63
|
+
Container({ columns: 12, gap: 16, children: "test" })
|
|
64
|
+
expect(mockProvide).toHaveBeenCalledTimes(1)
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
it("provides context with grid values", async () => {
|
|
68
|
+
const Container = (await import("../Container")).default
|
|
69
|
+
Container({ columns: 24, gap: 16, gutter: 8, children: "test" })
|
|
70
|
+
const config = mockProvide.mock.calls[0]?.[1] as Record<string, unknown>
|
|
71
|
+
expect(config.columns).toBe(24)
|
|
72
|
+
expect(config.gap).toBe(16)
|
|
73
|
+
expect(config.gutter).toBe(8)
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
it("uses width prop to override containerWidth", async () => {
|
|
77
|
+
const Container = (await import("../Container")).default
|
|
78
|
+
const result = asVNode(Container({ width: 960, children: "test" }))
|
|
79
|
+
expect((result.props.$coolgrid as Record<string, unknown>).width).toBe(960)
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
it("accepts width as function", async () => {
|
|
83
|
+
const Container = (await import("../Container")).default
|
|
84
|
+
const widthFn = (_containerWidth: any) => 800
|
|
85
|
+
const result = asVNode(Container({ width: widthFn as any, children: "test" }))
|
|
86
|
+
expect((result.props.$coolgrid as Record<string, unknown>).width).toBe(800)
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
it("strips context keys from DOM props", async () => {
|
|
90
|
+
const Container = (await import("../Container")).default
|
|
91
|
+
const result = asVNode(
|
|
92
|
+
Container({
|
|
93
|
+
columns: 12,
|
|
94
|
+
gap: 16,
|
|
95
|
+
"data-testid": "container",
|
|
96
|
+
children: "test",
|
|
97
|
+
}),
|
|
98
|
+
)
|
|
99
|
+
expect(result.props["data-testid"]).toBe("container")
|
|
100
|
+
})
|
|
101
|
+
|
|
102
|
+
it("passes css as extraStyles in $coolgrid", async () => {
|
|
103
|
+
const Container = (await import("../Container")).default
|
|
104
|
+
const customCss = "background: red;"
|
|
105
|
+
const result = asVNode(Container({ css: customCss, children: "test" }))
|
|
106
|
+
expect((result.props.$coolgrid as Record<string, unknown>).extraStyles).toBe(customCss)
|
|
107
|
+
})
|
|
108
|
+
|
|
109
|
+
it("provides context with all grid keys", async () => {
|
|
110
|
+
const Container = (await import("../Container")).default
|
|
111
|
+
Container({
|
|
112
|
+
columns: 12,
|
|
113
|
+
gap: 16,
|
|
114
|
+
gutter: 8,
|
|
115
|
+
padding: 4,
|
|
116
|
+
size: 6,
|
|
117
|
+
contentAlignX: "center",
|
|
118
|
+
children: "test",
|
|
119
|
+
})
|
|
120
|
+
const config = mockProvide.mock.calls[0]?.[1] as Record<string, unknown>
|
|
121
|
+
expect(config).toHaveProperty("columns")
|
|
122
|
+
expect(config).toHaveProperty("gap")
|
|
123
|
+
expect(config).toHaveProperty("gutter")
|
|
124
|
+
expect(config).toHaveProperty("padding")
|
|
125
|
+
expect(config).toHaveProperty("size")
|
|
126
|
+
expect(config).toHaveProperty("contentAlignX")
|
|
127
|
+
})
|
|
128
|
+
|
|
129
|
+
it("renders with data-coolgrid attribute in dev mode", async () => {
|
|
130
|
+
const Container = (await import("../Container")).default
|
|
131
|
+
const result = asVNode(Container({ children: "test" }))
|
|
132
|
+
expect(result.props["data-coolgrid"]).toBe("container")
|
|
133
|
+
})
|
|
134
|
+
|
|
135
|
+
it("passes component prop as 'as'", async () => {
|
|
136
|
+
const Container = (await import("../Container")).default
|
|
137
|
+
const customComponent = (() => null) as any
|
|
138
|
+
const result = asVNode(Container({ component: customComponent, children: "test" }))
|
|
139
|
+
expect(result.props.as).toBe(customComponent)
|
|
140
|
+
})
|
|
141
|
+
|
|
142
|
+
it("renders children in VNode", async () => {
|
|
143
|
+
const Container = (await import("../Container")).default
|
|
144
|
+
const result = asVNode(Container({ children: "hello world" }))
|
|
145
|
+
expect(result.children).toBeDefined()
|
|
146
|
+
})
|
|
147
|
+
})
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import type { VNode } from "@pyreon/core"
|
|
2
|
+
import { beforeEach, describe, expect, it, vi } from "vitest"
|
|
3
|
+
|
|
4
|
+
const mockProvide = vi.fn()
|
|
5
|
+
const mockUseContext = vi.fn()
|
|
6
|
+
|
|
7
|
+
vi.mock("@pyreon/core", async (importOriginal) => {
|
|
8
|
+
const original = await importOriginal<typeof import("@pyreon/core")>()
|
|
9
|
+
return {
|
|
10
|
+
...original,
|
|
11
|
+
provide: (...args: any[]) => {
|
|
12
|
+
mockProvide(...args)
|
|
13
|
+
},
|
|
14
|
+
useContext: (ctx: any) => {
|
|
15
|
+
if (mockUseContext.mock.calls.length > 0) {
|
|
16
|
+
return mockUseContext(ctx)
|
|
17
|
+
}
|
|
18
|
+
return original.useContext(ctx)
|
|
19
|
+
},
|
|
20
|
+
}
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
const asVNode = (v: unknown) => v as VNode
|
|
24
|
+
|
|
25
|
+
describe("Row", () => {
|
|
26
|
+
beforeEach(() => {
|
|
27
|
+
vi.clearAllMocks()
|
|
28
|
+
// unistyle context returns empty theme, container context returns empty
|
|
29
|
+
mockUseContext.mockReturnValue({})
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
it("returns a VNode", async () => {
|
|
33
|
+
const Row = (await import("../Row")).default
|
|
34
|
+
const result = asVNode(Row({ children: "test" }))
|
|
35
|
+
expect(result).toBeDefined()
|
|
36
|
+
expect(result.type).toBeDefined()
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
it("has correct displayName", async () => {
|
|
40
|
+
const Row = (await import("../Row")).default
|
|
41
|
+
expect(Row.displayName).toBe("@pyreon/coolgrid/Row")
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
it("has correct pkgName", async () => {
|
|
45
|
+
const Row = (await import("../Row")).default
|
|
46
|
+
expect(Row.pkgName).toBe("@pyreon/coolgrid")
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
it("has PYREON__COMPONENT static", async () => {
|
|
50
|
+
const Row = (await import("../Row")).default
|
|
51
|
+
expect(Row.PYREON__COMPONENT).toBe("@pyreon/coolgrid/Row")
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
it("passes $coolgrid prop with row values", async () => {
|
|
55
|
+
const Row = (await import("../Row")).default
|
|
56
|
+
const result = asVNode(Row({ gap: 16, children: "test" }))
|
|
57
|
+
expect(result.props).toHaveProperty("$coolgrid")
|
|
58
|
+
expect(result.props.$coolgrid).toHaveProperty("gap")
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
it("provides RowContext", async () => {
|
|
62
|
+
const Row = (await import("../Row")).default
|
|
63
|
+
Row({ gap: 16, columns: 12, children: "test" })
|
|
64
|
+
expect(mockProvide).toHaveBeenCalledTimes(1)
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
it("provides context with grid values", async () => {
|
|
68
|
+
const Row = (await import("../Row")).default
|
|
69
|
+
Row({ columns: 24, gap: 16, gutter: 8, children: "test" })
|
|
70
|
+
const config = mockProvide.mock.calls[0]?.[1] as Record<string, unknown>
|
|
71
|
+
expect(config.columns).toBe(24)
|
|
72
|
+
expect(config.gap).toBe(16)
|
|
73
|
+
expect(config.gutter).toBe(8)
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
it("passes contentAlignX to $coolgrid", async () => {
|
|
77
|
+
const Row = (await import("../Row")).default
|
|
78
|
+
const result = asVNode(Row({ contentAlignX: "center", children: "test" }))
|
|
79
|
+
expect((result.props.$coolgrid as Record<string, unknown>).contentAlignX).toBe("center")
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
it("strips context keys from DOM props", async () => {
|
|
83
|
+
const Row = (await import("../Row")).default
|
|
84
|
+
const result = asVNode(
|
|
85
|
+
Row({
|
|
86
|
+
columns: 12,
|
|
87
|
+
gap: 16,
|
|
88
|
+
"data-testid": "row",
|
|
89
|
+
children: "test",
|
|
90
|
+
}),
|
|
91
|
+
)
|
|
92
|
+
expect(result.props["data-testid"]).toBe("row")
|
|
93
|
+
})
|
|
94
|
+
|
|
95
|
+
it("passes css as extraStyles in $coolgrid when provided", async () => {
|
|
96
|
+
const Row = (await import("../Row")).default
|
|
97
|
+
const customCss = "background: blue;"
|
|
98
|
+
const result = asVNode(Row({ css: customCss, children: "test" }))
|
|
99
|
+
expect((result.props.$coolgrid as Record<string, unknown>).extraStyles).toBe(customCss)
|
|
100
|
+
})
|
|
101
|
+
|
|
102
|
+
it("passes gutter in $coolgrid", async () => {
|
|
103
|
+
const Row = (await import("../Row")).default
|
|
104
|
+
const result = asVNode(Row({ gutter: 24, children: "test" }))
|
|
105
|
+
expect((result.props.$coolgrid as Record<string, unknown>).gutter).toBe(24)
|
|
106
|
+
})
|
|
107
|
+
|
|
108
|
+
it("provides context including colCss and colComponent", async () => {
|
|
109
|
+
const Row = (await import("../Row")).default
|
|
110
|
+
const colComp = (() => null) as any
|
|
111
|
+
Row({ colCss: "color: red;", colComponent: colComp, children: "test" })
|
|
112
|
+
const config = mockProvide.mock.calls[0]?.[1] as Record<string, unknown>
|
|
113
|
+
expect(config.colCss).toBe("color: red;")
|
|
114
|
+
expect(config.colComponent).toBe(colComp)
|
|
115
|
+
})
|
|
116
|
+
|
|
117
|
+
it("renders with data-coolgrid attribute in dev mode", async () => {
|
|
118
|
+
const Row = (await import("../Row")).default
|
|
119
|
+
const result = asVNode(Row({ children: "test" }))
|
|
120
|
+
expect(result.props["data-coolgrid"]).toBe("row")
|
|
121
|
+
})
|
|
122
|
+
|
|
123
|
+
it("passes component prop as 'as'", async () => {
|
|
124
|
+
const Row = (await import("../Row")).default
|
|
125
|
+
const customComponent = (() => null) as any
|
|
126
|
+
const result = asVNode(Row({ component: customComponent, children: "test" }))
|
|
127
|
+
expect(result.props.as).toBe(customComponent)
|
|
128
|
+
})
|
|
129
|
+
|
|
130
|
+
it("renders children in VNode", async () => {
|
|
131
|
+
const Row = (await import("../Row")).default
|
|
132
|
+
const result = asVNode(Row({ children: "hello" }))
|
|
133
|
+
expect(result.children).toBeDefined()
|
|
134
|
+
})
|
|
135
|
+
})
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest"
|
|
2
|
+
|
|
3
|
+
describe("config", () => {
|
|
4
|
+
describe("theme defaults", () => {
|
|
5
|
+
it("has correct default theme", async () => {
|
|
6
|
+
const theme = (await import("../theme")).default
|
|
7
|
+
expect(theme).toEqual({
|
|
8
|
+
rootSize: 16,
|
|
9
|
+
breakpoints: {
|
|
10
|
+
xs: 0,
|
|
11
|
+
sm: 576,
|
|
12
|
+
md: 768,
|
|
13
|
+
lg: 992,
|
|
14
|
+
xl: 1200,
|
|
15
|
+
},
|
|
16
|
+
grid: {
|
|
17
|
+
columns: 12,
|
|
18
|
+
container: {
|
|
19
|
+
xs: "100%",
|
|
20
|
+
sm: 540,
|
|
21
|
+
md: 720,
|
|
22
|
+
lg: 960,
|
|
23
|
+
xl: 1140,
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
})
|
|
27
|
+
})
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
describe("defaultBreakpoints", () => {
|
|
31
|
+
it("has all standard breakpoints", async () => {
|
|
32
|
+
const { defaultBreakpoints } = await import("../theme")
|
|
33
|
+
expect(defaultBreakpoints).toEqual({
|
|
34
|
+
xs: 0,
|
|
35
|
+
sm: 576,
|
|
36
|
+
md: 768,
|
|
37
|
+
lg: 992,
|
|
38
|
+
xl: 1200,
|
|
39
|
+
})
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
it("breakpoints are in ascending order", async () => {
|
|
43
|
+
const { defaultBreakpoints } = await import("../theme")
|
|
44
|
+
const values = Object.values(defaultBreakpoints)
|
|
45
|
+
for (let i = 1; i < values.length; i++) {
|
|
46
|
+
expect(values[i]).toBeGreaterThan(values[i - 1] as number)
|
|
47
|
+
}
|
|
48
|
+
})
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
describe("defaultContainerWidths", () => {
|
|
52
|
+
it("has widths for all breakpoints", async () => {
|
|
53
|
+
const { defaultContainerWidths } = await import("../theme")
|
|
54
|
+
expect(defaultContainerWidths).toEqual({
|
|
55
|
+
xs: "100%",
|
|
56
|
+
sm: 540,
|
|
57
|
+
md: 720,
|
|
58
|
+
lg: 960,
|
|
59
|
+
xl: 1140,
|
|
60
|
+
})
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
it("xs is percentage, others are numbers", async () => {
|
|
64
|
+
const { defaultContainerWidths } = await import("../theme")
|
|
65
|
+
expect(typeof defaultContainerWidths.xs).toBe("string")
|
|
66
|
+
expect(typeof defaultContainerWidths.sm).toBe("number")
|
|
67
|
+
expect(typeof defaultContainerWidths.md).toBe("number")
|
|
68
|
+
expect(typeof defaultContainerWidths.lg).toBe("number")
|
|
69
|
+
expect(typeof defaultContainerWidths.xl).toBe("number")
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
it("numeric widths are in ascending order", async () => {
|
|
73
|
+
const { defaultContainerWidths } = await import("../theme")
|
|
74
|
+
const numericWidths = [
|
|
75
|
+
defaultContainerWidths.sm,
|
|
76
|
+
defaultContainerWidths.md,
|
|
77
|
+
defaultContainerWidths.lg,
|
|
78
|
+
defaultContainerWidths.xl,
|
|
79
|
+
] as number[]
|
|
80
|
+
for (let i = 1; i < numericWidths.length; i++) {
|
|
81
|
+
expect(numericWidths[i]).toBeGreaterThan(numericWidths[i - 1] as number)
|
|
82
|
+
}
|
|
83
|
+
})
|
|
84
|
+
})
|
|
85
|
+
|
|
86
|
+
describe("ContainerContext", () => {
|
|
87
|
+
it("is created via createContext with an id", async () => {
|
|
88
|
+
const { ContainerContext } = await import("../context")
|
|
89
|
+
expect(ContainerContext).toHaveProperty("id")
|
|
90
|
+
})
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
describe("RowContext", () => {
|
|
94
|
+
it("is created via createContext with an id", async () => {
|
|
95
|
+
const { RowContext } = await import("../context")
|
|
96
|
+
expect(RowContext).toHaveProperty("id")
|
|
97
|
+
})
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
describe("constants", () => {
|
|
101
|
+
it("has correct PKG_NAME", async () => {
|
|
102
|
+
const { PKG_NAME } = await import("../constants")
|
|
103
|
+
expect(PKG_NAME).toBe("@pyreon/coolgrid")
|
|
104
|
+
})
|
|
105
|
+
|
|
106
|
+
it("has CONTEXT_KEYS", async () => {
|
|
107
|
+
const { CONTEXT_KEYS } = await import("../constants")
|
|
108
|
+
expect(CONTEXT_KEYS).toContain("columns")
|
|
109
|
+
expect(CONTEXT_KEYS).toContain("size")
|
|
110
|
+
expect(CONTEXT_KEYS).toContain("gap")
|
|
111
|
+
expect(CONTEXT_KEYS).toContain("padding")
|
|
112
|
+
expect(CONTEXT_KEYS).toContain("gutter")
|
|
113
|
+
expect(CONTEXT_KEYS).toContain("colCss")
|
|
114
|
+
expect(CONTEXT_KEYS).toContain("colComponent")
|
|
115
|
+
expect(CONTEXT_KEYS).toContain("rowCss")
|
|
116
|
+
expect(CONTEXT_KEYS).toContain("rowComponent")
|
|
117
|
+
expect(CONTEXT_KEYS).toContain("contentAlignX")
|
|
118
|
+
})
|
|
119
|
+
})
|
|
120
|
+
})
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import type { VNode } from "@pyreon/core"
|
|
2
|
+
import { beforeEach, describe, expect, it, vi } from "vitest"
|
|
3
|
+
|
|
4
|
+
const mockProvide = vi.fn()
|
|
5
|
+
const mockUseContext = vi.fn()
|
|
6
|
+
|
|
7
|
+
vi.mock("@pyreon/core", async (importOriginal) => {
|
|
8
|
+
const original = await importOriginal<typeof import("@pyreon/core")>()
|
|
9
|
+
return {
|
|
10
|
+
...original,
|
|
11
|
+
provide: (...args: any[]) => {
|
|
12
|
+
mockProvide(...args)
|
|
13
|
+
},
|
|
14
|
+
useContext: (...args: any[]) => {
|
|
15
|
+
return mockUseContext(...args)
|
|
16
|
+
},
|
|
17
|
+
}
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
const asVNode = (v: unknown) => v as VNode
|
|
21
|
+
|
|
22
|
+
describe("Context cascading: Container -> Row -> Col", () => {
|
|
23
|
+
beforeEach(() => {
|
|
24
|
+
vi.clearAllMocks()
|
|
25
|
+
// Default: unistyle theme context returns empty theme
|
|
26
|
+
mockUseContext.mockReturnValue({ theme: {} })
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
it("Container provides context with grid config", async () => {
|
|
30
|
+
const Container = (await import("../Container")).default
|
|
31
|
+
// Container calls useContext once (for unistyle theme)
|
|
32
|
+
Container({ columns: 12, gap: 16, gutter: 8, padding: 4, children: "test" })
|
|
33
|
+
|
|
34
|
+
expect(mockProvide).toHaveBeenCalledTimes(1)
|
|
35
|
+
const config = mockProvide.mock.calls[0]?.[1] as Record<string, unknown>
|
|
36
|
+
|
|
37
|
+
expect(config.columns).toBe(12)
|
|
38
|
+
expect(config.gap).toBe(16)
|
|
39
|
+
expect(config.gutter).toBe(8)
|
|
40
|
+
expect(config.padding).toBe(4)
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
it("Row reads Container context and provides its own", async () => {
|
|
44
|
+
const Row = (await import("../Row")).default
|
|
45
|
+
|
|
46
|
+
// Row calls useContext twice:
|
|
47
|
+
// 1st: ContainerContext (grid config from parent)
|
|
48
|
+
// 2nd: unistyle context (theme) inside useGridContext
|
|
49
|
+
mockUseContext
|
|
50
|
+
.mockReturnValueOnce({
|
|
51
|
+
columns: 12,
|
|
52
|
+
gap: 16,
|
|
53
|
+
gutter: 8,
|
|
54
|
+
padding: 4,
|
|
55
|
+
})
|
|
56
|
+
.mockReturnValueOnce({ theme: {} })
|
|
57
|
+
|
|
58
|
+
Row({ children: "test" })
|
|
59
|
+
|
|
60
|
+
const rowConfig = mockProvide.mock.calls[0]?.[1] as Record<string, unknown>
|
|
61
|
+
|
|
62
|
+
expect(rowConfig.columns).toBe(12)
|
|
63
|
+
expect(rowConfig.gap).toBe(16)
|
|
64
|
+
expect(rowConfig.gutter).toBe(8)
|
|
65
|
+
expect(rowConfig.padding).toBe(4)
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
it("Row can override Container values", async () => {
|
|
69
|
+
const Row = (await import("../Row")).default
|
|
70
|
+
|
|
71
|
+
// 1st call: ContainerContext, 2nd call: unistyle theme
|
|
72
|
+
mockUseContext.mockReturnValueOnce({ columns: 12, gap: 8 }).mockReturnValueOnce({ theme: {} })
|
|
73
|
+
|
|
74
|
+
Row({ columns: 24, gap: 32, children: "test" })
|
|
75
|
+
|
|
76
|
+
const rowConfig = mockProvide.mock.calls[0]?.[1] as Record<string, unknown>
|
|
77
|
+
|
|
78
|
+
expect(rowConfig.columns).toBe(24)
|
|
79
|
+
expect(rowConfig.gap).toBe(32)
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
it("Col reads Row context and passes $coolgrid", async () => {
|
|
83
|
+
const Col = (await import("../Col")).default
|
|
84
|
+
|
|
85
|
+
// Col calls useContext twice:
|
|
86
|
+
// 1st: RowContext, 2nd: unistyle theme inside useGridContext
|
|
87
|
+
mockUseContext.mockReturnValueOnce({ columns: 12, gap: 20 }).mockReturnValueOnce({ theme: {} })
|
|
88
|
+
|
|
89
|
+
const result = asVNode(Col({ size: 4, children: "test" }))
|
|
90
|
+
expect(result.props.$coolgrid).toBeDefined()
|
|
91
|
+
expect((result.props.$coolgrid as Record<string, unknown>).size).toBe(4)
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
it("Col does not provide context", async () => {
|
|
95
|
+
const Col = (await import("../Col")).default
|
|
96
|
+
|
|
97
|
+
Col({ size: 6, children: "test" })
|
|
98
|
+
expect(mockProvide).not.toHaveBeenCalled()
|
|
99
|
+
})
|
|
100
|
+
|
|
101
|
+
it("Container calls provide", async () => {
|
|
102
|
+
const Container = (await import("../Container")).default
|
|
103
|
+
Container({ children: "test" })
|
|
104
|
+
|
|
105
|
+
expect(mockProvide).toHaveBeenCalledTimes(1)
|
|
106
|
+
})
|
|
107
|
+
|
|
108
|
+
it("Row calls provide", async () => {
|
|
109
|
+
const Row = (await import("../Row")).default
|
|
110
|
+
Row({ children: "test" })
|
|
111
|
+
|
|
112
|
+
expect(mockProvide).toHaveBeenCalledTimes(1)
|
|
113
|
+
})
|
|
114
|
+
})
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest"
|
|
2
|
+
|
|
3
|
+
describe("index exports", () => {
|
|
4
|
+
it("exports Container", async () => {
|
|
5
|
+
const mod = await import("../index")
|
|
6
|
+
expect(mod.Container).toBeDefined()
|
|
7
|
+
expect(typeof mod.Container).toBe("function")
|
|
8
|
+
})
|
|
9
|
+
|
|
10
|
+
it("exports Row", async () => {
|
|
11
|
+
const mod = await import("../index")
|
|
12
|
+
expect(mod.Row).toBeDefined()
|
|
13
|
+
expect(typeof mod.Row).toBe("function")
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
it("exports Col", async () => {
|
|
17
|
+
const mod = await import("../index")
|
|
18
|
+
expect(mod.Col).toBeDefined()
|
|
19
|
+
expect(typeof mod.Col).toBe("function")
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
it("exports Provider", async () => {
|
|
23
|
+
const mod = await import("../index")
|
|
24
|
+
expect(mod.Provider).toBeDefined()
|
|
25
|
+
expect(typeof mod.Provider).toBe("function")
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
it("exports theme", async () => {
|
|
29
|
+
const mod = await import("../index")
|
|
30
|
+
expect(mod.theme).toBeDefined()
|
|
31
|
+
expect(mod.theme).toHaveProperty("rootSize")
|
|
32
|
+
expect(mod.theme).toHaveProperty("breakpoints")
|
|
33
|
+
expect(mod.theme).toHaveProperty("grid")
|
|
34
|
+
})
|
|
35
|
+
})
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { beforeEach, describe, expect, it, vi } from "vitest"
|
|
2
|
+
|
|
3
|
+
const mockUseContext = vi.fn()
|
|
4
|
+
|
|
5
|
+
vi.mock("@pyreon/core", async (importOriginal) => {
|
|
6
|
+
const original = await importOriginal<typeof import("@pyreon/core")>()
|
|
7
|
+
return {
|
|
8
|
+
...original,
|
|
9
|
+
useContext: mockUseContext,
|
|
10
|
+
}
|
|
11
|
+
})
|
|
12
|
+
|
|
13
|
+
describe("useGridContext", () => {
|
|
14
|
+
beforeEach(() => {
|
|
15
|
+
vi.clearAllMocks()
|
|
16
|
+
// Default: empty theme context
|
|
17
|
+
mockUseContext.mockReturnValue({ theme: {} })
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
it("returns props merged with theme grid context", async () => {
|
|
21
|
+
mockUseContext.mockReturnValue({
|
|
22
|
+
theme: {
|
|
23
|
+
grid: {
|
|
24
|
+
columns: 12,
|
|
25
|
+
container: { xs: "100%", md: 720 },
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
})
|
|
29
|
+
const useGridContext = (await import("../useContext")).default
|
|
30
|
+
const result = useGridContext({ gap: 16 })
|
|
31
|
+
expect(result.gap).toBe(16)
|
|
32
|
+
expect(result.columns).toBe(12)
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
it("props override theme values", async () => {
|
|
36
|
+
mockUseContext.mockReturnValue({
|
|
37
|
+
theme: {
|
|
38
|
+
grid: { columns: 12 },
|
|
39
|
+
},
|
|
40
|
+
})
|
|
41
|
+
const useGridContext = (await import("../useContext")).default
|
|
42
|
+
const result = useGridContext({ columns: 24 })
|
|
43
|
+
expect(result.columns).toBe(24)
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
it("falls back to coolgrid namespace in theme", async () => {
|
|
47
|
+
mockUseContext.mockReturnValue({
|
|
48
|
+
theme: {
|
|
49
|
+
coolgrid: {
|
|
50
|
+
columns: 16,
|
|
51
|
+
container: { xs: "100%" },
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
})
|
|
55
|
+
const useGridContext = (await import("../useContext")).default
|
|
56
|
+
const result = useGridContext({})
|
|
57
|
+
expect(result.columns).toBe(16)
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
it("returns empty context when no theme or props", async () => {
|
|
61
|
+
mockUseContext.mockReturnValue({ theme: {} })
|
|
62
|
+
const useGridContext = (await import("../useContext")).default
|
|
63
|
+
const result = useGridContext({})
|
|
64
|
+
expect(result).toBeDefined()
|
|
65
|
+
})
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
describe("getGridContext", () => {
|
|
69
|
+
it("resolves columns from props first", async () => {
|
|
70
|
+
const { getGridContext } = await import("../useContext")
|
|
71
|
+
const result = getGridContext({ columns: 24 }, { grid: { columns: 12 } })
|
|
72
|
+
expect(result.columns).toBe(24)
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
it("resolves columns from theme.grid", async () => {
|
|
76
|
+
const { getGridContext } = await import("../useContext")
|
|
77
|
+
const result = getGridContext({}, { grid: { columns: 12 } })
|
|
78
|
+
expect(result.columns).toBe(12)
|
|
79
|
+
})
|
|
80
|
+
|
|
81
|
+
it("resolves columns from theme.coolgrid", async () => {
|
|
82
|
+
const { getGridContext } = await import("../useContext")
|
|
83
|
+
const result = getGridContext({}, { coolgrid: { columns: 16 } })
|
|
84
|
+
expect(result.columns).toBe(16)
|
|
85
|
+
})
|
|
86
|
+
|
|
87
|
+
it("resolves containerWidth from theme.grid.container", async () => {
|
|
88
|
+
const { getGridContext } = await import("../useContext")
|
|
89
|
+
const result = getGridContext({}, { grid: { container: { xs: "100%" } } })
|
|
90
|
+
expect(result.containerWidth).toEqual({ xs: "100%" })
|
|
91
|
+
})
|
|
92
|
+
})
|