@pyreon/elements 0.11.1 → 0.11.3

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.
Files changed (52) hide show
  1. package/package.json +8 -7
  2. package/src/Element/component.tsx +211 -0
  3. package/src/Element/constants.ts +96 -0
  4. package/src/Element/index.ts +6 -0
  5. package/src/Element/types.ts +168 -0
  6. package/src/Element/utils.ts +15 -0
  7. package/src/List/component.tsx +57 -0
  8. package/src/List/index.ts +5 -0
  9. package/src/Overlay/component.tsx +131 -0
  10. package/src/Overlay/context.tsx +37 -0
  11. package/src/Overlay/index.ts +7 -0
  12. package/src/Overlay/useOverlay.tsx +616 -0
  13. package/src/Portal/component.tsx +41 -0
  14. package/src/Portal/index.ts +5 -0
  15. package/src/Text/component.tsx +65 -0
  16. package/src/Text/index.ts +5 -0
  17. package/src/Text/styled.ts +30 -0
  18. package/src/Util/component.tsx +43 -0
  19. package/src/Util/index.ts +5 -0
  20. package/src/__tests__/Content.test.tsx +115 -0
  21. package/src/__tests__/Element.test.ts +604 -0
  22. package/src/__tests__/Iterator.test.ts +483 -0
  23. package/src/__tests__/List.test.ts +199 -0
  24. package/src/__tests__/Overlay.test.ts +485 -0
  25. package/src/__tests__/Portal.test.ts +82 -0
  26. package/src/__tests__/Text.test.ts +274 -0
  27. package/src/__tests__/Util.test.ts +63 -0
  28. package/src/__tests__/Wrapper.test.tsx +152 -0
  29. package/src/__tests__/equalBeforeAfter.test.ts +122 -0
  30. package/src/__tests__/helpers.test.ts +65 -0
  31. package/src/__tests__/overlayContext.test.tsx +78 -0
  32. package/src/__tests__/responsiveProps.test.ts +298 -0
  33. package/src/__tests__/useOverlay.test.ts +1330 -0
  34. package/src/__tests__/utils.test.ts +69 -0
  35. package/src/constants.ts +1 -0
  36. package/src/helpers/Content/component.tsx +51 -0
  37. package/src/helpers/Content/index.ts +3 -0
  38. package/src/helpers/Content/styled.ts +105 -0
  39. package/src/helpers/Content/types.ts +49 -0
  40. package/src/helpers/Iterator/component.tsx +252 -0
  41. package/src/helpers/Iterator/index.ts +13 -0
  42. package/src/helpers/Iterator/types.ts +79 -0
  43. package/src/helpers/Wrapper/component.tsx +78 -0
  44. package/src/helpers/Wrapper/constants.ts +10 -0
  45. package/src/helpers/Wrapper/index.ts +3 -0
  46. package/src/helpers/Wrapper/styled.ts +69 -0
  47. package/src/helpers/Wrapper/types.ts +56 -0
  48. package/src/helpers/Wrapper/utils.ts +7 -0
  49. package/src/helpers/index.ts +4 -0
  50. package/src/index.ts +37 -0
  51. package/src/types.ts +81 -0
  52. package/src/utils.ts +1 -0
@@ -0,0 +1,78 @@
1
+ import { popContext } from "@pyreon/core"
2
+ import { afterEach, describe, expect, it, vi } from "vitest"
3
+ import OverlayContextProvider, { useOverlayContext } from "../Overlay/context"
4
+
5
+ describe("Overlay context", () => {
6
+ it("useOverlayContext is a function", () => {
7
+ expect(typeof useOverlayContext).toBe("function")
8
+ })
9
+
10
+ it("returns the default context (empty object) when called outside a provider", () => {
11
+ const ctx = useOverlayContext()
12
+ expect(ctx).toEqual({})
13
+ })
14
+ })
15
+
16
+ describe("OverlayContextProvider component", () => {
17
+ afterEach(() => {
18
+ try {
19
+ popContext()
20
+ } catch {
21
+ // Ignore if no context was pushed
22
+ }
23
+ })
24
+
25
+ it("provides blocked/setBlocked/setUnblocked via context", () => {
26
+ const setBlocked = vi.fn()
27
+ const setUnblocked = vi.fn()
28
+
29
+ OverlayContextProvider({
30
+ blocked: true,
31
+ setBlocked,
32
+ setUnblocked,
33
+ children: "child",
34
+ })
35
+
36
+ const ctx = useOverlayContext()
37
+ expect(ctx.blocked).toBe(true)
38
+ expect(ctx.setBlocked).toBe(setBlocked)
39
+ expect(ctx.setUnblocked).toBe(setUnblocked)
40
+ })
41
+
42
+ it("renders children (returns a value)", () => {
43
+ const result = OverlayContextProvider({
44
+ blocked: false,
45
+ setBlocked: vi.fn(),
46
+ setUnblocked: vi.fn(),
47
+ children: "Hello overlay",
48
+ })
49
+
50
+ expect(result).toBeDefined()
51
+ })
52
+
53
+ it("provides blocked as false", () => {
54
+ OverlayContextProvider({
55
+ blocked: false,
56
+ setBlocked: vi.fn(),
57
+ setUnblocked: vi.fn(),
58
+ children: null,
59
+ })
60
+
61
+ const ctx = useOverlayContext()
62
+ expect(ctx.blocked).toBe(false)
63
+ })
64
+
65
+ it("provides blocked as a function when passed as a function", () => {
66
+ const blockedFn = () => true
67
+
68
+ OverlayContextProvider({
69
+ blocked: blockedFn,
70
+ setBlocked: vi.fn(),
71
+ setUnblocked: vi.fn(),
72
+ children: null,
73
+ })
74
+
75
+ const ctx = useOverlayContext()
76
+ expect(ctx.blocked).toBe(blockedFn)
77
+ })
78
+ })
@@ -0,0 +1,298 @@
1
+ import type { VNode } from "@pyreon/core"
2
+ import { h } from "@pyreon/core"
3
+ import { describe, expect, it } from "vitest"
4
+ import { Element } from "../Element"
5
+ import Content from "../helpers/Content/component"
6
+ import Wrapper from "../helpers/Wrapper/component"
7
+
8
+ const asVNode = (v: unknown) => v as VNode
9
+
10
+ const getContentSlots = (result: VNode): VNode[] => {
11
+ const children = result.props.children
12
+ if (!Array.isArray(children)) return []
13
+ return children.filter(
14
+ (c: unknown) =>
15
+ c != null && typeof c === "object" && "type" in (c as VNode) && (c as VNode).type === Content,
16
+ ) as VNode[]
17
+ }
18
+
19
+ describe("Element responsive props", () => {
20
+ describe("single values", () => {
21
+ it("renders with alignX as string", () => {
22
+ const result = asVNode(Element({ alignX: "center", children: "content" }))
23
+ expect(result.type).toBe(Wrapper)
24
+ })
25
+
26
+ it("renders with alignY as string", () => {
27
+ const result = asVNode(Element({ alignY: "top", children: "content" }))
28
+ expect(result.type).toBe(Wrapper)
29
+ })
30
+
31
+ it("renders with direction as string", () => {
32
+ const result = asVNode(Element({ direction: "rows", children: "content" }))
33
+ expect(result.type).toBe(Wrapper)
34
+ })
35
+
36
+ it("renders with gap as number", () => {
37
+ const result = asVNode(
38
+ Element({
39
+ gap: 16,
40
+ beforeContent: h("span", null, "Before"),
41
+ afterContent: h("span", null, "After"),
42
+ children: "content",
43
+ }),
44
+ )
45
+ expect(result.type).toBe(Wrapper)
46
+ })
47
+
48
+ it("renders with block as boolean", () => {
49
+ const result = asVNode(Element({ block: true, children: "content" }))
50
+ expect(result.type).toBe(Wrapper)
51
+ expect(result.props.block).toBe(true)
52
+ })
53
+
54
+ it("renders with equalCols as boolean", () => {
55
+ const result = asVNode(
56
+ Element({
57
+ equalCols: true,
58
+ beforeContent: h("span", null, "Before"),
59
+ afterContent: h("span", null, "After"),
60
+ children: "content",
61
+ }),
62
+ )
63
+ expect(result.type).toBe(Wrapper)
64
+ })
65
+ })
66
+
67
+ describe("array values (positional breakpoints)", () => {
68
+ it("renders with alignX as array", () => {
69
+ const result = asVNode(
70
+ Element({ alignX: ["left", "center", "right"] as any, children: "content" }),
71
+ )
72
+ expect(result.type).toBe(Wrapper)
73
+ })
74
+
75
+ it("renders with alignY as array", () => {
76
+ const result = asVNode(
77
+ Element({ alignY: ["top", "center", "bottom"] as any, children: "content" }),
78
+ )
79
+ expect(result.type).toBe(Wrapper)
80
+ })
81
+
82
+ it("renders with direction as array", () => {
83
+ const result = asVNode(Element({ direction: ["rows", "inline"] as any, children: "content" }))
84
+ expect(result.type).toBe(Wrapper)
85
+ })
86
+
87
+ it("renders with gap as array", () => {
88
+ const result = asVNode(
89
+ Element({
90
+ gap: [8, 16, 24] as any,
91
+ beforeContent: h("span", null, "Before"),
92
+ children: "content",
93
+ }),
94
+ )
95
+ expect(result.type).toBe(Wrapper)
96
+ })
97
+
98
+ it("renders with block as array", () => {
99
+ const result = asVNode(Element({ block: [false, true] as any, children: "content" }))
100
+ expect(result.type).toBe(Wrapper)
101
+ })
102
+
103
+ it("renders with equalCols as array", () => {
104
+ const result = asVNode(
105
+ Element({
106
+ equalCols: [false, true] as any,
107
+ beforeContent: h("span", null, "Before"),
108
+ afterContent: h("span", null, "After"),
109
+ children: "content",
110
+ }),
111
+ )
112
+ expect(result.type).toBe(Wrapper)
113
+ })
114
+ })
115
+
116
+ describe("breakpoint object values", () => {
117
+ it("renders with alignX as breakpoint object", () => {
118
+ const result = asVNode(
119
+ Element({
120
+ alignX: { xs: "left", md: "center", xl: "right" } as any,
121
+ children: "content",
122
+ }),
123
+ )
124
+ expect(result.type).toBe(Wrapper)
125
+ })
126
+
127
+ it("renders with alignY as breakpoint object", () => {
128
+ const result = asVNode(
129
+ Element({ alignY: { xs: "top", lg: "center" } as any, children: "content" }),
130
+ )
131
+ expect(result.type).toBe(Wrapper)
132
+ })
133
+
134
+ it("renders with direction as breakpoint object", () => {
135
+ const result = asVNode(
136
+ Element({
137
+ direction: { xs: "rows", md: "inline" } as any,
138
+ children: "content",
139
+ }),
140
+ )
141
+ expect(result.type).toBe(Wrapper)
142
+ })
143
+
144
+ it("renders with gap as breakpoint object", () => {
145
+ const result = asVNode(
146
+ Element({
147
+ gap: { xs: 8, md: 16, lg: 24 } as any,
148
+ beforeContent: h("span", null, "Before"),
149
+ children: "content",
150
+ }),
151
+ )
152
+ expect(result.type).toBe(Wrapper)
153
+ })
154
+
155
+ it("renders with block as breakpoint object", () => {
156
+ const result = asVNode(
157
+ Element({ block: { xs: false, md: true } as any, children: "content" }),
158
+ )
159
+ expect(result.type).toBe(Wrapper)
160
+ })
161
+ })
162
+
163
+ describe("combined responsive props", () => {
164
+ it("renders with multiple responsive props simultaneously", () => {
165
+ const result = asVNode(
166
+ Element({
167
+ alignX: { xs: "left", md: "center" } as any,
168
+ alignY: ["top", "center"] as any,
169
+ direction: { xs: "rows", lg: "inline" } as any,
170
+ block: [false, true] as any,
171
+ gap: 16,
172
+ beforeContent: h("span", { "data-testid": "before" }, "Before"),
173
+ afterContent: h("span", { "data-testid": "after" }, "After"),
174
+ children: h("span", { "data-testid": "main" }, "Main"),
175
+ }),
176
+ )
177
+ expect(result.type).toBe(Wrapper)
178
+ const slots = getContentSlots(result)
179
+ expect(slots).toHaveLength(3)
180
+ })
181
+
182
+ it("renders with responsive content directions", () => {
183
+ const result = asVNode(
184
+ Element({
185
+ contentDirection: { xs: "rows", md: "inline" } as any,
186
+ beforeContentDirection: { xs: "inline", lg: "rows" } as any,
187
+ afterContentDirection: "inline",
188
+ beforeContent: h("span", null, "Before"),
189
+ afterContent: h("span", null, "After"),
190
+ children: h("span", null, "Main"),
191
+ }),
192
+ )
193
+ expect(result.type).toBe(Wrapper)
194
+ const slots = getContentSlots(result)
195
+ expect(slots).toHaveLength(3)
196
+ })
197
+
198
+ it("renders with responsive content alignment", () => {
199
+ const result = asVNode(
200
+ Element({
201
+ contentAlignX: { xs: "left", md: "center" } as any,
202
+ contentAlignY: ["top", "center", "bottom"] as any,
203
+ beforeContentAlignX: "left",
204
+ afterContentAlignX: "right",
205
+ beforeContent: h("span", null, "Before"),
206
+ afterContent: h("span", null, "After"),
207
+ children: "content",
208
+ }),
209
+ )
210
+ expect(result.type).toBe(Wrapper)
211
+ })
212
+ })
213
+
214
+ describe("responsive css prop", () => {
215
+ it("renders with css as string", () => {
216
+ const result = asVNode(Element({ css: "background: red;", children: "content" }))
217
+ expect(result.type).toBe(Wrapper)
218
+ expect(result.props.extendCss).toBe("background: red;")
219
+ })
220
+
221
+ it("renders with contentCss", () => {
222
+ const result = asVNode(
223
+ Element({
224
+ contentCss: "color: blue;",
225
+ beforeContent: h("span", null, "Before"),
226
+ children: "content",
227
+ }),
228
+ )
229
+ expect(result.type).toBe(Wrapper)
230
+ const slots = getContentSlots(result)
231
+ const contentSlot = slots.find((v) => v.props.contentType === "content")
232
+ expect(contentSlot?.props.extendCss).toBe("color: blue;")
233
+ })
234
+
235
+ it("renders with beforeContentCss and afterContentCss", () => {
236
+ const result = asVNode(
237
+ Element({
238
+ beforeContentCss: "padding: 4px;",
239
+ afterContentCss: "padding: 8px;",
240
+ beforeContent: h("span", null, "Before"),
241
+ afterContent: h("span", null, "After"),
242
+ children: "content",
243
+ }),
244
+ )
245
+ expect(result.type).toBe(Wrapper)
246
+ const slots = getContentSlots(result)
247
+ const beforeSlot = slots.find((v) => v.props.contentType === "before")
248
+ const afterSlot = slots.find((v) => v.props.contentType === "after")
249
+ expect(beforeSlot?.props.extendCss).toBe("padding: 4px;")
250
+ expect(afterSlot?.props.extendCss).toBe("padding: 8px;")
251
+ })
252
+ })
253
+
254
+ describe("alignment values", () => {
255
+ const alignXValues = [
256
+ "left",
257
+ "center",
258
+ "right",
259
+ "spaceBetween",
260
+ "spaceAround",
261
+ "block",
262
+ ] as const
263
+
264
+ const alignYValues = [
265
+ "top",
266
+ "center",
267
+ "bottom",
268
+ "spaceBetween",
269
+ "spaceAround",
270
+ "block",
271
+ ] as const
272
+
273
+ for (const value of alignXValues) {
274
+ it(`renders with alignX="${value}"`, () => {
275
+ const result = asVNode(Element({ alignX: value, children: "content" }))
276
+ expect(result.type).toBe(Wrapper)
277
+ })
278
+ }
279
+
280
+ for (const value of alignYValues) {
281
+ it(`renders with alignY="${value}"`, () => {
282
+ const result = asVNode(Element({ alignY: value, children: "content" }))
283
+ expect(result.type).toBe(Wrapper)
284
+ })
285
+ }
286
+ })
287
+
288
+ describe("direction values", () => {
289
+ const directionValues = ["inline", "rows", "reverseInline", "reverseRows"] as const
290
+
291
+ for (const value of directionValues) {
292
+ it(`renders with direction="${value}"`, () => {
293
+ const result = asVNode(Element({ direction: value, children: "content" }))
294
+ expect(result.type).toBe(Wrapper)
295
+ })
296
+ }
297
+ })
298
+ })