@pyreon/elements 0.11.4 → 0.11.6
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/README.md +38 -35
- package/lib/index.d.ts +15 -15
- package/package.json +24 -24
- package/src/Element/component.tsx +14 -14
- package/src/Element/constants.ts +25 -25
- package/src/Element/index.ts +2 -2
- package/src/Element/types.ts +3 -3
- package/src/Element/utils.ts +1 -1
- package/src/List/component.tsx +7 -7
- package/src/List/index.ts +2 -2
- package/src/Overlay/component.tsx +22 -22
- package/src/Overlay/context.tsx +2 -2
- package/src/Overlay/index.ts +3 -3
- package/src/Overlay/useOverlay.tsx +97 -97
- package/src/Portal/component.tsx +6 -6
- package/src/Portal/index.ts +2 -2
- package/src/Text/component.tsx +6 -6
- package/src/Text/index.ts +2 -2
- package/src/Text/styled.ts +4 -4
- package/src/Util/component.tsx +5 -5
- package/src/Util/index.ts +2 -2
- package/src/__tests__/Content.test.tsx +46 -46
- package/src/__tests__/Element.test.ts +251 -251
- package/src/__tests__/Iterator.test.ts +142 -142
- package/src/__tests__/List.test.ts +61 -61
- package/src/__tests__/Overlay.test.ts +125 -125
- package/src/__tests__/Portal.test.ts +33 -33
- package/src/__tests__/Text.test.ts +128 -128
- package/src/__tests__/Util.test.ts +31 -31
- package/src/__tests__/Wrapper.test.tsx +60 -60
- package/src/__tests__/equalBeforeAfter.test.ts +41 -41
- package/src/__tests__/helpers.test.ts +29 -29
- package/src/__tests__/overlayContext.test.tsx +14 -14
- package/src/__tests__/responsiveProps.test.ts +116 -116
- package/src/__tests__/useOverlay.test.ts +283 -283
- package/src/__tests__/utils.test.ts +43 -43
- package/src/constants.ts +1 -1
- package/src/helpers/Content/component.tsx +5 -5
- package/src/helpers/Content/index.ts +1 -1
- package/src/helpers/Content/styled.ts +16 -16
- package/src/helpers/Content/types.ts +7 -7
- package/src/helpers/Iterator/component.tsx +28 -28
- package/src/helpers/Iterator/index.ts +2 -2
- package/src/helpers/Iterator/types.ts +3 -3
- package/src/helpers/Wrapper/component.tsx +6 -6
- package/src/helpers/Wrapper/index.ts +1 -1
- package/src/helpers/Wrapper/styled.ts +8 -8
- package/src/helpers/Wrapper/types.ts +3 -3
- package/src/helpers/Wrapper/utils.ts +1 -1
- package/src/helpers/index.ts +2 -2
- package/src/index.ts +16 -16
- package/src/types.ts +7 -7
- package/src/utils.ts +1 -1
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import type { VNode } from
|
|
2
|
-
import { h } from
|
|
3
|
-
import { describe, expect, it } from
|
|
4
|
-
import { Element } from
|
|
5
|
-
import Content from
|
|
6
|
-
import Wrapper from
|
|
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
7
|
|
|
8
8
|
const asVNode = (v: unknown) => v as VNode
|
|
9
9
|
|
|
@@ -16,142 +16,142 @@ const getContentSlots = (result: VNode): VNode[] => {
|
|
|
16
16
|
if (!Array.isArray(children)) return []
|
|
17
17
|
return children.filter(
|
|
18
18
|
(c: unknown) =>
|
|
19
|
-
c != null && typeof c ===
|
|
19
|
+
c != null && typeof c === 'object' && 'type' in (c as VNode) && (c as VNode).type === Content,
|
|
20
20
|
) as VNode[]
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
-
describe(
|
|
24
|
-
describe(
|
|
25
|
-
it(
|
|
26
|
-
const result = asVNode(Element({ children:
|
|
27
|
-
expect(typeof result.type).toBe(
|
|
23
|
+
describe('Element', () => {
|
|
24
|
+
describe('basic rendering', () => {
|
|
25
|
+
it('returns a VNode whose type is the Wrapper component (a function)', () => {
|
|
26
|
+
const result = asVNode(Element({ children: 'hello' }))
|
|
27
|
+
expect(typeof result.type).toBe('function')
|
|
28
28
|
expect(result.type).toBe(Wrapper)
|
|
29
29
|
})
|
|
30
30
|
|
|
31
|
-
it(
|
|
32
|
-
const result = asVNode(Element({ tag:
|
|
33
|
-
expect(result.props.tag).toBe(
|
|
31
|
+
it('passes tag as the tag prop to Wrapper', () => {
|
|
32
|
+
const result = asVNode(Element({ tag: 'section', children: 'content' }))
|
|
33
|
+
expect(result.props.tag).toBe('section')
|
|
34
34
|
})
|
|
35
35
|
|
|
36
|
-
it(
|
|
37
|
-
const result = asVNode(Element({ children:
|
|
36
|
+
it('defaults tag to undefined when not specified', () => {
|
|
37
|
+
const result = asVNode(Element({ children: 'hello' }))
|
|
38
38
|
expect(result.props.tag).toBeUndefined()
|
|
39
39
|
})
|
|
40
40
|
|
|
41
|
-
it(
|
|
41
|
+
it('renders with no children', () => {
|
|
42
42
|
const result = asVNode(Element({}))
|
|
43
43
|
expect(result.type).toBe(Wrapper)
|
|
44
44
|
})
|
|
45
45
|
})
|
|
46
46
|
|
|
47
|
-
describe(
|
|
48
|
-
it(
|
|
49
|
-
const result = asVNode(Element({ children:
|
|
50
|
-
expect(result.props.direction).toBe(
|
|
47
|
+
describe('simple element (no beforeContent/afterContent)', () => {
|
|
48
|
+
it('uses contentDirection as wrapper direction (defaults to rows)', () => {
|
|
49
|
+
const result = asVNode(Element({ children: 'test' }))
|
|
50
|
+
expect(result.props.direction).toBe('rows')
|
|
51
51
|
})
|
|
52
52
|
|
|
53
|
-
it(
|
|
54
|
-
const result = asVNode(Element({ children:
|
|
55
|
-
expect(result.props.alignX).toBe(
|
|
53
|
+
it('uses contentAlignX as wrapper alignX (defaults to left)', () => {
|
|
54
|
+
const result = asVNode(Element({ children: 'test' }))
|
|
55
|
+
expect(result.props.alignX).toBe('left')
|
|
56
56
|
})
|
|
57
57
|
|
|
58
|
-
it(
|
|
59
|
-
const result = asVNode(Element({ children:
|
|
60
|
-
expect(result.props.alignY).toBe(
|
|
58
|
+
it('uses contentAlignY as wrapper alignY (defaults to center)', () => {
|
|
59
|
+
const result = asVNode(Element({ children: 'test' }))
|
|
60
|
+
expect(result.props.alignY).toBe('center')
|
|
61
61
|
})
|
|
62
62
|
|
|
63
|
-
it(
|
|
64
|
-
const result = asVNode(Element({ contentDirection:
|
|
65
|
-
expect(result.props.direction).toBe(
|
|
63
|
+
it('overrides direction with contentDirection when simple', () => {
|
|
64
|
+
const result = asVNode(Element({ contentDirection: 'inline', children: 'test' }))
|
|
65
|
+
expect(result.props.direction).toBe('inline')
|
|
66
66
|
})
|
|
67
67
|
|
|
68
|
-
it(
|
|
69
|
-
const result = asVNode(Element({ contentAlignX:
|
|
70
|
-
expect(result.props.alignX).toBe(
|
|
68
|
+
it('overrides alignX with contentAlignX when simple', () => {
|
|
69
|
+
const result = asVNode(Element({ contentAlignX: 'center', children: 'test' }))
|
|
70
|
+
expect(result.props.alignX).toBe('center')
|
|
71
71
|
})
|
|
72
72
|
|
|
73
|
-
it(
|
|
74
|
-
const result = asVNode(Element({ contentAlignY:
|
|
75
|
-
expect(result.props.alignY).toBe(
|
|
73
|
+
it('overrides alignY with contentAlignY when simple', () => {
|
|
74
|
+
const result = asVNode(Element({ contentAlignY: 'top', children: 'test' }))
|
|
75
|
+
expect(result.props.alignY).toBe('top')
|
|
76
76
|
})
|
|
77
77
|
|
|
78
|
-
it(
|
|
79
|
-
const result = asVNode(Element({ children: h(
|
|
78
|
+
it('renders children directly via render() without Content wrappers', () => {
|
|
79
|
+
const result = asVNode(Element({ children: h('span', null, 'inner') }))
|
|
80
80
|
const slots = getContentSlots(result)
|
|
81
81
|
expect(slots).toHaveLength(0)
|
|
82
82
|
})
|
|
83
83
|
|
|
84
|
-
it(
|
|
85
|
-
const result = asVNode(Element({ children:
|
|
84
|
+
it('renders string children in props.children array', () => {
|
|
85
|
+
const result = asVNode(Element({ children: 'hello' }))
|
|
86
86
|
const children = result.props.children as unknown[]
|
|
87
87
|
// Simple element renders: [falsy beforeContent, render(CHILDREN), falsy afterContent]
|
|
88
88
|
expect(children).toBeDefined()
|
|
89
89
|
expect(Array.isArray(children)).toBe(true)
|
|
90
90
|
})
|
|
91
91
|
|
|
92
|
-
it(
|
|
93
|
-
const result = asVNode(Element({ block: true, children:
|
|
92
|
+
it('passes block prop to Wrapper', () => {
|
|
93
|
+
const result = asVNode(Element({ block: true, children: 'test' }))
|
|
94
94
|
expect(result.props.block).toBe(true)
|
|
95
95
|
})
|
|
96
96
|
})
|
|
97
97
|
|
|
98
|
-
describe(
|
|
99
|
-
it(
|
|
98
|
+
describe('three-section layout (with beforeContent/afterContent)', () => {
|
|
99
|
+
it('defaults wrapper direction to inline', () => {
|
|
100
100
|
const result = asVNode(
|
|
101
101
|
Element({
|
|
102
|
-
beforeContent: h(
|
|
103
|
-
children:
|
|
104
|
-
afterContent: h(
|
|
102
|
+
beforeContent: h('span', null, 'B'),
|
|
103
|
+
children: 'test',
|
|
104
|
+
afterContent: h('span', null, 'A'),
|
|
105
105
|
}),
|
|
106
106
|
)
|
|
107
|
-
expect(result.props.direction).toBe(
|
|
107
|
+
expect(result.props.direction).toBe('inline')
|
|
108
108
|
})
|
|
109
109
|
|
|
110
|
-
it(
|
|
110
|
+
it('uses explicit direction when provided', () => {
|
|
111
111
|
const result = asVNode(
|
|
112
112
|
Element({
|
|
113
|
-
direction:
|
|
114
|
-
beforeContent: h(
|
|
115
|
-
children:
|
|
116
|
-
afterContent: h(
|
|
113
|
+
direction: 'rows',
|
|
114
|
+
beforeContent: h('span', null, 'B'),
|
|
115
|
+
children: 'test',
|
|
116
|
+
afterContent: h('span', null, 'A'),
|
|
117
117
|
}),
|
|
118
118
|
)
|
|
119
|
-
expect(result.props.direction).toBe(
|
|
119
|
+
expect(result.props.direction).toBe('rows')
|
|
120
120
|
})
|
|
121
121
|
|
|
122
|
-
it(
|
|
122
|
+
it('uses default alignX (left) and alignY (center)', () => {
|
|
123
123
|
const result = asVNode(
|
|
124
124
|
Element({
|
|
125
|
-
beforeContent:
|
|
126
|
-
children:
|
|
127
|
-
afterContent:
|
|
125
|
+
beforeContent: 'B',
|
|
126
|
+
children: 'test',
|
|
127
|
+
afterContent: 'A',
|
|
128
128
|
}),
|
|
129
129
|
)
|
|
130
|
-
expect(result.props.alignX).toBe(
|
|
131
|
-
expect(result.props.alignY).toBe(
|
|
130
|
+
expect(result.props.alignX).toBe('left')
|
|
131
|
+
expect(result.props.alignY).toBe('center')
|
|
132
132
|
})
|
|
133
133
|
|
|
134
|
-
it(
|
|
134
|
+
it('uses explicit alignX and alignY', () => {
|
|
135
135
|
const result = asVNode(
|
|
136
136
|
Element({
|
|
137
|
-
alignX:
|
|
138
|
-
alignY:
|
|
139
|
-
beforeContent:
|
|
140
|
-
children:
|
|
141
|
-
afterContent:
|
|
137
|
+
alignX: 'center',
|
|
138
|
+
alignY: 'top',
|
|
139
|
+
beforeContent: 'B',
|
|
140
|
+
children: 'test',
|
|
141
|
+
afterContent: 'A',
|
|
142
142
|
}),
|
|
143
143
|
)
|
|
144
|
-
expect(result.props.alignX).toBe(
|
|
145
|
-
expect(result.props.alignY).toBe(
|
|
144
|
+
expect(result.props.alignX).toBe('center')
|
|
145
|
+
expect(result.props.alignY).toBe('top')
|
|
146
146
|
})
|
|
147
147
|
|
|
148
|
-
it(
|
|
149
|
-
const before = h(
|
|
150
|
-
const after = h(
|
|
148
|
+
it('renders three Content children when both before and after exist', () => {
|
|
149
|
+
const before = h('span', null, 'Before')
|
|
150
|
+
const after = h('span', null, 'After')
|
|
151
151
|
const result = asVNode(
|
|
152
152
|
Element({
|
|
153
153
|
beforeContent: before,
|
|
154
|
-
children:
|
|
154
|
+
children: 'Main',
|
|
155
155
|
afterContent: after,
|
|
156
156
|
}),
|
|
157
157
|
)
|
|
@@ -160,46 +160,46 @@ describe("Element", () => {
|
|
|
160
160
|
expect(slots).toHaveLength(3)
|
|
161
161
|
})
|
|
162
162
|
|
|
163
|
-
it(
|
|
164
|
-
const before = h(
|
|
165
|
-
const after = h(
|
|
163
|
+
it('sets correct contentType on each Content slot', () => {
|
|
164
|
+
const before = h('span', null, 'Before')
|
|
165
|
+
const after = h('span', null, 'After')
|
|
166
166
|
const result = asVNode(
|
|
167
167
|
Element({
|
|
168
168
|
beforeContent: before,
|
|
169
|
-
children:
|
|
169
|
+
children: 'Main',
|
|
170
170
|
afterContent: after,
|
|
171
171
|
}),
|
|
172
172
|
)
|
|
173
173
|
|
|
174
174
|
const slots = getContentSlots(result)
|
|
175
175
|
const [slot0, slot1, slot2] = slots as [VNode, VNode, VNode]
|
|
176
|
-
expect(slot0.props.contentType).toBe(
|
|
177
|
-
expect(slot1.props.contentType).toBe(
|
|
178
|
-
expect(slot2.props.contentType).toBe(
|
|
176
|
+
expect(slot0.props.contentType).toBe('before')
|
|
177
|
+
expect(slot1.props.contentType).toBe('content')
|
|
178
|
+
expect(slot2.props.contentType).toBe('after')
|
|
179
179
|
})
|
|
180
180
|
|
|
181
|
-
it(
|
|
181
|
+
it('passes parentDirection to Content slots', () => {
|
|
182
182
|
const result = asVNode(
|
|
183
183
|
Element({
|
|
184
|
-
direction:
|
|
185
|
-
beforeContent:
|
|
186
|
-
children:
|
|
187
|
-
afterContent:
|
|
184
|
+
direction: 'rows',
|
|
185
|
+
beforeContent: 'B',
|
|
186
|
+
children: 'M',
|
|
187
|
+
afterContent: 'A',
|
|
188
188
|
}),
|
|
189
189
|
)
|
|
190
190
|
|
|
191
191
|
const slots = getContentSlots(result)
|
|
192
192
|
for (const slot of slots) {
|
|
193
|
-
expect(slot.props.parentDirection).toBe(
|
|
193
|
+
expect(slot.props.parentDirection).toBe('rows')
|
|
194
194
|
}
|
|
195
195
|
})
|
|
196
196
|
|
|
197
|
-
it(
|
|
198
|
-
const before = h(
|
|
197
|
+
it('renders before and content Content slots when no afterContent', () => {
|
|
198
|
+
const before = h('span', null, 'Before')
|
|
199
199
|
const result = asVNode(
|
|
200
200
|
Element({
|
|
201
201
|
beforeContent: before,
|
|
202
|
-
children:
|
|
202
|
+
children: 'Main',
|
|
203
203
|
}),
|
|
204
204
|
)
|
|
205
205
|
|
|
@@ -207,15 +207,15 @@ describe("Element", () => {
|
|
|
207
207
|
// beforeContent makes isSimpleElement false, so content also gets a Content wrapper
|
|
208
208
|
expect(slots).toHaveLength(2)
|
|
209
209
|
const [s0, s1] = slots as [VNode, VNode]
|
|
210
|
-
expect(s0.props.contentType).toBe(
|
|
211
|
-
expect(s1.props.contentType).toBe(
|
|
210
|
+
expect(s0.props.contentType).toBe('before')
|
|
211
|
+
expect(s1.props.contentType).toBe('content')
|
|
212
212
|
})
|
|
213
213
|
|
|
214
|
-
it(
|
|
215
|
-
const after = h(
|
|
214
|
+
it('renders content and after Content slots when no beforeContent', () => {
|
|
215
|
+
const after = h('span', null, 'After')
|
|
216
216
|
const result = asVNode(
|
|
217
217
|
Element({
|
|
218
|
-
children:
|
|
218
|
+
children: 'Main',
|
|
219
219
|
afterContent: after,
|
|
220
220
|
}),
|
|
221
221
|
)
|
|
@@ -224,33 +224,33 @@ describe("Element", () => {
|
|
|
224
224
|
// content slot + after slot (both are Content wrappers since afterContent makes it non-simple)
|
|
225
225
|
expect(slots).toHaveLength(2)
|
|
226
226
|
const [c0, c1] = slots as [VNode, VNode]
|
|
227
|
-
expect(c0.props.contentType).toBe(
|
|
228
|
-
expect(c1.props.contentType).toBe(
|
|
227
|
+
expect(c0.props.contentType).toBe('content')
|
|
228
|
+
expect(c1.props.contentType).toBe('after')
|
|
229
229
|
})
|
|
230
230
|
|
|
231
|
-
it(
|
|
231
|
+
it('uses span sub-tag for inline parent elements (like span)', () => {
|
|
232
232
|
const result = asVNode(
|
|
233
233
|
Element({
|
|
234
|
-
tag:
|
|
235
|
-
beforeContent:
|
|
236
|
-
children:
|
|
237
|
-
afterContent:
|
|
234
|
+
tag: 'span',
|
|
235
|
+
beforeContent: 'B',
|
|
236
|
+
children: 'M',
|
|
237
|
+
afterContent: 'A',
|
|
238
238
|
}),
|
|
239
239
|
)
|
|
240
240
|
|
|
241
241
|
const slots = getContentSlots(result)
|
|
242
242
|
for (const slot of slots) {
|
|
243
|
-
expect(slot.props.tag).toBe(
|
|
243
|
+
expect(slot.props.tag).toBe('span')
|
|
244
244
|
}
|
|
245
245
|
})
|
|
246
246
|
|
|
247
|
-
it(
|
|
247
|
+
it('uses undefined sub-tag for block parent elements (like div)', () => {
|
|
248
248
|
const result = asVNode(
|
|
249
249
|
Element({
|
|
250
|
-
tag:
|
|
251
|
-
beforeContent:
|
|
252
|
-
children:
|
|
253
|
-
afterContent:
|
|
250
|
+
tag: 'div',
|
|
251
|
+
beforeContent: 'B',
|
|
252
|
+
children: 'M',
|
|
253
|
+
afterContent: 'A',
|
|
254
254
|
}),
|
|
255
255
|
)
|
|
256
256
|
|
|
@@ -260,13 +260,13 @@ describe("Element", () => {
|
|
|
260
260
|
}
|
|
261
261
|
})
|
|
262
262
|
|
|
263
|
-
it(
|
|
263
|
+
it('passes equalCols to Content slots', () => {
|
|
264
264
|
const result = asVNode(
|
|
265
265
|
Element({
|
|
266
266
|
equalCols: true,
|
|
267
|
-
beforeContent:
|
|
268
|
-
children:
|
|
269
|
-
afterContent:
|
|
267
|
+
beforeContent: 'B',
|
|
268
|
+
children: 'M',
|
|
269
|
+
afterContent: 'A',
|
|
270
270
|
}),
|
|
271
271
|
)
|
|
272
272
|
|
|
@@ -276,155 +276,155 @@ describe("Element", () => {
|
|
|
276
276
|
}
|
|
277
277
|
})
|
|
278
278
|
|
|
279
|
-
it(
|
|
279
|
+
it('passes gap to before and after Content slots but not content slot', () => {
|
|
280
280
|
const result = asVNode(
|
|
281
281
|
Element({
|
|
282
282
|
gap: 16,
|
|
283
|
-
beforeContent:
|
|
284
|
-
children:
|
|
285
|
-
afterContent:
|
|
283
|
+
beforeContent: 'B',
|
|
284
|
+
children: 'M',
|
|
285
|
+
afterContent: 'A',
|
|
286
286
|
}),
|
|
287
287
|
)
|
|
288
288
|
|
|
289
289
|
const slots = getContentSlots(result)
|
|
290
|
-
const beforeSlot = slots.find((v) => v.props.contentType ===
|
|
291
|
-
const contentSlot = slots.find((v) => v.props.contentType ===
|
|
292
|
-
const afterSlot = slots.find((v) => v.props.contentType ===
|
|
290
|
+
const beforeSlot = slots.find((v) => v.props.contentType === 'before')
|
|
291
|
+
const contentSlot = slots.find((v) => v.props.contentType === 'content')
|
|
292
|
+
const afterSlot = slots.find((v) => v.props.contentType === 'after')
|
|
293
293
|
|
|
294
294
|
expect(beforeSlot?.props.gap).toBe(16)
|
|
295
295
|
expect(contentSlot?.props.gap).toBeUndefined()
|
|
296
296
|
expect(afterSlot?.props.gap).toBe(16)
|
|
297
297
|
})
|
|
298
298
|
|
|
299
|
-
it(
|
|
299
|
+
it('passes content-level alignment to the content Content slot', () => {
|
|
300
300
|
const result = asVNode(
|
|
301
301
|
Element({
|
|
302
|
-
contentDirection:
|
|
303
|
-
contentAlignX:
|
|
304
|
-
contentAlignY:
|
|
305
|
-
beforeContent:
|
|
306
|
-
children:
|
|
307
|
-
afterContent:
|
|
302
|
+
contentDirection: 'inline',
|
|
303
|
+
contentAlignX: 'center',
|
|
304
|
+
contentAlignY: 'top',
|
|
305
|
+
beforeContent: 'B',
|
|
306
|
+
children: 'M',
|
|
307
|
+
afterContent: 'A',
|
|
308
308
|
}),
|
|
309
309
|
)
|
|
310
310
|
|
|
311
311
|
const slots = getContentSlots(result)
|
|
312
|
-
const contentSlot = slots.find((v) => v.props.contentType ===
|
|
313
|
-
expect(contentSlot?.props.direction).toBe(
|
|
314
|
-
expect(contentSlot?.props.alignX).toBe(
|
|
315
|
-
expect(contentSlot?.props.alignY).toBe(
|
|
312
|
+
const contentSlot = slots.find((v) => v.props.contentType === 'content')
|
|
313
|
+
expect(contentSlot?.props.direction).toBe('inline')
|
|
314
|
+
expect(contentSlot?.props.alignX).toBe('center')
|
|
315
|
+
expect(contentSlot?.props.alignY).toBe('top')
|
|
316
316
|
})
|
|
317
317
|
|
|
318
|
-
it(
|
|
318
|
+
it('passes before-level alignment to the before Content slot', () => {
|
|
319
319
|
const result = asVNode(
|
|
320
320
|
Element({
|
|
321
|
-
beforeContentDirection:
|
|
322
|
-
beforeContentAlignX:
|
|
323
|
-
beforeContentAlignY:
|
|
324
|
-
beforeContent:
|
|
325
|
-
children:
|
|
326
|
-
afterContent:
|
|
321
|
+
beforeContentDirection: 'rows',
|
|
322
|
+
beforeContentAlignX: 'right',
|
|
323
|
+
beforeContentAlignY: 'bottom',
|
|
324
|
+
beforeContent: 'B',
|
|
325
|
+
children: 'M',
|
|
326
|
+
afterContent: 'A',
|
|
327
327
|
}),
|
|
328
328
|
)
|
|
329
329
|
|
|
330
330
|
const slots = getContentSlots(result)
|
|
331
|
-
const beforeSlot = slots.find((v) => v.props.contentType ===
|
|
332
|
-
expect(beforeSlot?.props.direction).toBe(
|
|
333
|
-
expect(beforeSlot?.props.alignX).toBe(
|
|
334
|
-
expect(beforeSlot?.props.alignY).toBe(
|
|
331
|
+
const beforeSlot = slots.find((v) => v.props.contentType === 'before')
|
|
332
|
+
expect(beforeSlot?.props.direction).toBe('rows')
|
|
333
|
+
expect(beforeSlot?.props.alignX).toBe('right')
|
|
334
|
+
expect(beforeSlot?.props.alignY).toBe('bottom')
|
|
335
335
|
})
|
|
336
336
|
|
|
337
|
-
it(
|
|
337
|
+
it('passes after-level alignment to the after Content slot', () => {
|
|
338
338
|
const result = asVNode(
|
|
339
339
|
Element({
|
|
340
|
-
afterContentDirection:
|
|
341
|
-
afterContentAlignX:
|
|
342
|
-
afterContentAlignY:
|
|
343
|
-
beforeContent:
|
|
344
|
-
children:
|
|
345
|
-
afterContent:
|
|
340
|
+
afterContentDirection: 'rows',
|
|
341
|
+
afterContentAlignX: 'center',
|
|
342
|
+
afterContentAlignY: 'top',
|
|
343
|
+
beforeContent: 'B',
|
|
344
|
+
children: 'M',
|
|
345
|
+
afterContent: 'A',
|
|
346
346
|
}),
|
|
347
347
|
)
|
|
348
348
|
|
|
349
349
|
const slots = getContentSlots(result)
|
|
350
|
-
const afterSlot = slots.find((v) => v.props.contentType ===
|
|
351
|
-
expect(afterSlot?.props.direction).toBe(
|
|
352
|
-
expect(afterSlot?.props.alignX).toBe(
|
|
353
|
-
expect(afterSlot?.props.alignY).toBe(
|
|
350
|
+
const afterSlot = slots.find((v) => v.props.contentType === 'after')
|
|
351
|
+
expect(afterSlot?.props.direction).toBe('rows')
|
|
352
|
+
expect(afterSlot?.props.alignX).toBe('center')
|
|
353
|
+
expect(afterSlot?.props.alignY).toBe('top')
|
|
354
354
|
})
|
|
355
355
|
})
|
|
356
356
|
|
|
357
|
-
describe(
|
|
358
|
-
it(
|
|
359
|
-
const result = asVNode(Element({ id:
|
|
360
|
-
expect(result.props.id).toBe(
|
|
357
|
+
describe('HTML attribute filtering', () => {
|
|
358
|
+
it('passes through id', () => {
|
|
359
|
+
const result = asVNode(Element({ id: 'my-el', children: 'test' }))
|
|
360
|
+
expect(result.props.id).toBe('my-el')
|
|
361
361
|
})
|
|
362
362
|
|
|
363
|
-
it(
|
|
364
|
-
const result = asVNode(Element({ role:
|
|
365
|
-
expect(result.props.role).toBe(
|
|
363
|
+
it('passes through role', () => {
|
|
364
|
+
const result = asVNode(Element({ role: 'button', children: 'test' }))
|
|
365
|
+
expect(result.props.role).toBe('button')
|
|
366
366
|
})
|
|
367
367
|
|
|
368
|
-
it(
|
|
369
|
-
const result = asVNode(Element({
|
|
370
|
-
expect(result.props[
|
|
368
|
+
it('passes through data- attributes', () => {
|
|
369
|
+
const result = asVNode(Element({ 'data-testid': 'el', children: 'test' }))
|
|
370
|
+
expect(result.props['data-testid']).toBe('el')
|
|
371
371
|
})
|
|
372
372
|
|
|
373
|
-
it(
|
|
374
|
-
const result = asVNode(Element({
|
|
375
|
-
expect(result.props[
|
|
373
|
+
it('passes through aria- attributes', () => {
|
|
374
|
+
const result = asVNode(Element({ 'aria-label': 'label', children: 'test' }))
|
|
375
|
+
expect(result.props['aria-label']).toBe('label')
|
|
376
376
|
})
|
|
377
377
|
|
|
378
|
-
it(
|
|
378
|
+
it('passes through on-prefixed event handlers', () => {
|
|
379
379
|
const handler = () => {
|
|
380
380
|
/* noop */
|
|
381
381
|
}
|
|
382
|
-
const result = asVNode(Element({ onClick: handler, children:
|
|
382
|
+
const result = asVNode(Element({ onClick: handler, children: 'test' }))
|
|
383
383
|
expect(result.props.onClick).toBe(handler)
|
|
384
384
|
})
|
|
385
385
|
|
|
386
|
-
it(
|
|
386
|
+
it('passes through tabindex', () => {
|
|
387
387
|
// @ts-expect-error — testing element-specific attr forwarding
|
|
388
|
-
const result = asVNode(Element({ tabindex: 0, children:
|
|
388
|
+
const result = asVNode(Element({ tabindex: 0, children: 'test' }))
|
|
389
389
|
expect(result.props.tabindex).toBe(0)
|
|
390
390
|
})
|
|
391
391
|
|
|
392
|
-
it(
|
|
393
|
-
const result = asVNode(Element({ title:
|
|
394
|
-
expect(result.props.title).toBe(
|
|
392
|
+
it('passes through title', () => {
|
|
393
|
+
const result = asVNode(Element({ title: 'tooltip', children: 'test' }))
|
|
394
|
+
expect(result.props.title).toBe('tooltip')
|
|
395
395
|
})
|
|
396
396
|
|
|
397
|
-
it(
|
|
397
|
+
it('passes through href for anchor tag', () => {
|
|
398
398
|
// @ts-expect-error — testing element-specific attr forwarding
|
|
399
|
-
const result = asVNode(Element({ tag:
|
|
400
|
-
expect(result.props.href).toBe(
|
|
399
|
+
const result = asVNode(Element({ tag: 'a', href: '/link', children: 'test' }))
|
|
400
|
+
expect(result.props.href).toBe('/link')
|
|
401
401
|
})
|
|
402
402
|
|
|
403
|
-
it(
|
|
403
|
+
it('passes through disabled for button tag', () => {
|
|
404
404
|
// @ts-expect-error — testing element-specific attr forwarding
|
|
405
|
-
const result = asVNode(Element({ tag:
|
|
405
|
+
const result = asVNode(Element({ tag: 'button', disabled: true, children: 'test' }))
|
|
406
406
|
expect(result.props.disabled).toBe(true)
|
|
407
407
|
})
|
|
408
408
|
|
|
409
|
-
it(
|
|
410
|
-
const result = asVNode(Element({ class:
|
|
411
|
-
expect(result.props.class).toBe(
|
|
409
|
+
it('passes through class', () => {
|
|
410
|
+
const result = asVNode(Element({ class: 'my-class', children: 'test' }))
|
|
411
|
+
expect(result.props.class).toBe('my-class')
|
|
412
412
|
})
|
|
413
413
|
|
|
414
|
-
it(
|
|
415
|
-
const result = asVNode(Element({ children:
|
|
414
|
+
it('does not set class when not provided', () => {
|
|
415
|
+
const result = asVNode(Element({ children: 'test' }))
|
|
416
416
|
expect(result.props.class).toBeUndefined()
|
|
417
417
|
})
|
|
418
418
|
|
|
419
|
-
it(
|
|
419
|
+
it('filters out reserved props (gap, beforeContent, afterContent, css, etc.)', () => {
|
|
420
420
|
const result = asVNode(
|
|
421
421
|
Element({
|
|
422
|
-
beforeContent: h(
|
|
423
|
-
afterContent: h(
|
|
424
|
-
children:
|
|
425
|
-
direction:
|
|
426
|
-
alignX:
|
|
427
|
-
alignY:
|
|
422
|
+
beforeContent: h('span', null, 'x'),
|
|
423
|
+
afterContent: h('span', null, 'y'),
|
|
424
|
+
children: 'test',
|
|
425
|
+
direction: 'inline',
|
|
426
|
+
alignX: 'center',
|
|
427
|
+
alignY: 'center',
|
|
428
428
|
gap: 8,
|
|
429
429
|
block: true,
|
|
430
430
|
equalCols: true,
|
|
@@ -442,162 +442,162 @@ describe("Element", () => {
|
|
|
442
442
|
})
|
|
443
443
|
})
|
|
444
444
|
|
|
445
|
-
describe(
|
|
446
|
-
it(
|
|
447
|
-
const result = asVNode(Element({ children:
|
|
448
|
-
expect(typeof result.props.ref).toBe(
|
|
445
|
+
describe('ref handling', () => {
|
|
446
|
+
it('passes a merged ref function to Wrapper', () => {
|
|
447
|
+
const result = asVNode(Element({ children: 'test' }))
|
|
448
|
+
expect(typeof result.props.ref).toBe('function')
|
|
449
449
|
})
|
|
450
450
|
|
|
451
|
-
it(
|
|
451
|
+
it('wraps function ref in mergedRef', () => {
|
|
452
452
|
let captured: HTMLElement | null = null
|
|
453
453
|
const ref = (node: HTMLElement | null) => {
|
|
454
454
|
captured = node
|
|
455
455
|
}
|
|
456
|
-
const result = asVNode(Element({ ref, children:
|
|
457
|
-
expect(typeof result.props.ref).toBe(
|
|
456
|
+
const result = asVNode(Element({ ref, children: 'test' }))
|
|
457
|
+
expect(typeof result.props.ref).toBe('function')
|
|
458
458
|
const fakeNode = {} as HTMLElement
|
|
459
459
|
;(result.props.ref as (node: HTMLElement | null) => void)(fakeNode)
|
|
460
460
|
expect(captured).toBe(fakeNode)
|
|
461
461
|
})
|
|
462
462
|
|
|
463
|
-
it(
|
|
463
|
+
it('wraps object ref in mergedRef', () => {
|
|
464
464
|
const ref = { current: null as HTMLElement | null }
|
|
465
|
-
const result = asVNode(Element({ ref, children:
|
|
466
|
-
expect(typeof result.props.ref).toBe(
|
|
465
|
+
const result = asVNode(Element({ ref, children: 'test' }))
|
|
466
|
+
expect(typeof result.props.ref).toBe('function')
|
|
467
467
|
const fakeNode = {} as HTMLElement
|
|
468
468
|
;(result.props.ref as (node: HTMLElement | null) => void)(fakeNode)
|
|
469
469
|
expect(ref.current).toBe(fakeNode)
|
|
470
470
|
})
|
|
471
471
|
})
|
|
472
472
|
|
|
473
|
-
describe(
|
|
474
|
-
it(
|
|
473
|
+
describe('void / empty elements', () => {
|
|
474
|
+
it('renders img with no children', () => {
|
|
475
475
|
// @ts-expect-error — testing element-specific attr forwarding
|
|
476
|
-
const result = asVNode(Element({ tag:
|
|
476
|
+
const result = asVNode(Element({ tag: 'img', src: '/pic.png' }))
|
|
477
477
|
expect(result.type).toBe(Wrapper)
|
|
478
|
-
expect(result.props.tag).toBe(
|
|
479
|
-
expect(result.props.src).toBe(
|
|
478
|
+
expect(result.props.tag).toBe('img')
|
|
479
|
+
expect(result.props.src).toBe('/pic.png')
|
|
480
480
|
expect(result.props.children).toBeUndefined()
|
|
481
481
|
})
|
|
482
482
|
|
|
483
|
-
it(
|
|
483
|
+
it('renders input with no children', () => {
|
|
484
484
|
// @ts-expect-error — testing element-specific attr forwarding
|
|
485
|
-
const result = asVNode(Element({ tag:
|
|
485
|
+
const result = asVNode(Element({ tag: 'input', type: 'text' }))
|
|
486
486
|
expect(result.type).toBe(Wrapper)
|
|
487
|
-
expect(result.props.tag).toBe(
|
|
488
|
-
expect(result.props.type).toBe(
|
|
487
|
+
expect(result.props.tag).toBe('input')
|
|
488
|
+
expect(result.props.type).toBe('text')
|
|
489
489
|
expect(result.props.children).toBeUndefined()
|
|
490
490
|
})
|
|
491
491
|
|
|
492
|
-
it(
|
|
493
|
-
const result = asVNode(Element({ dangerouslySetInnerHTML: { __html:
|
|
492
|
+
it('renders with dangerouslySetInnerHTML (treated as empty)', () => {
|
|
493
|
+
const result = asVNode(Element({ dangerouslySetInnerHTML: { __html: '<b>hi</b>' } }))
|
|
494
494
|
expect(result.type).toBe(Wrapper)
|
|
495
|
-
expect(result.props.dangerouslySetInnerHTML).toEqual({ __html:
|
|
495
|
+
expect(result.props.dangerouslySetInnerHTML).toEqual({ __html: '<b>hi</b>' })
|
|
496
496
|
expect(result.props.children).toBeUndefined()
|
|
497
497
|
})
|
|
498
498
|
|
|
499
|
-
it(
|
|
500
|
-
const result = asVNode(Element({ tag:
|
|
499
|
+
it('renders br with no children', () => {
|
|
500
|
+
const result = asVNode(Element({ tag: 'br' }))
|
|
501
501
|
expect(result.type).toBe(Wrapper)
|
|
502
502
|
expect(result.props.children).toBeUndefined()
|
|
503
503
|
})
|
|
504
504
|
|
|
505
|
-
it(
|
|
506
|
-
const result = asVNode(Element({ tag:
|
|
505
|
+
it('renders hr with no children', () => {
|
|
506
|
+
const result = asVNode(Element({ tag: 'hr' }))
|
|
507
507
|
expect(result.type).toBe(Wrapper)
|
|
508
508
|
expect(result.props.children).toBeUndefined()
|
|
509
509
|
})
|
|
510
510
|
})
|
|
511
511
|
|
|
512
|
-
describe(
|
|
513
|
-
it(
|
|
514
|
-
const result = asVNode(Element({ tag:
|
|
512
|
+
describe('isInline flag for Wrapper', () => {
|
|
513
|
+
it('passes isInline=true for inline tags like span', () => {
|
|
514
|
+
const result = asVNode(Element({ tag: 'span', children: 'text' }))
|
|
515
515
|
expect(result.props.isInline).toBe(true)
|
|
516
516
|
})
|
|
517
517
|
|
|
518
|
-
it(
|
|
518
|
+
it('passes isInline=true for anchor tag', () => {
|
|
519
519
|
// @ts-expect-error — testing element-specific attr forwarding
|
|
520
|
-
const result = asVNode(Element({ tag:
|
|
520
|
+
const result = asVNode(Element({ tag: 'a', href: '#', children: 'link' }))
|
|
521
521
|
expect(result.props.isInline).toBe(true)
|
|
522
522
|
})
|
|
523
523
|
|
|
524
|
-
it(
|
|
525
|
-
const result = asVNode(Element({ tag:
|
|
524
|
+
it('passes isInline=false for block tags like section', () => {
|
|
525
|
+
const result = asVNode(Element({ tag: 'section', children: 'text' }))
|
|
526
526
|
expect(result.props.isInline).toBe(false)
|
|
527
527
|
})
|
|
528
528
|
|
|
529
|
-
it(
|
|
530
|
-
const result = asVNode(Element({ children:
|
|
529
|
+
it('passes isInline=false when tag is undefined (default)', () => {
|
|
530
|
+
const result = asVNode(Element({ children: 'text' }))
|
|
531
531
|
expect(result.props.isInline).toBe(false)
|
|
532
532
|
})
|
|
533
533
|
})
|
|
534
534
|
|
|
535
|
-
describe(
|
|
536
|
-
it(
|
|
537
|
-
const customCss =
|
|
538
|
-
const result = asVNode(Element({ css: customCss, children:
|
|
535
|
+
describe('extendCss prop', () => {
|
|
536
|
+
it('passes css prop as extendCss to Wrapper', () => {
|
|
537
|
+
const customCss = 'color: red;'
|
|
538
|
+
const result = asVNode(Element({ css: customCss, children: 'test' }))
|
|
539
539
|
expect(result.props.extendCss).toBe(customCss)
|
|
540
540
|
})
|
|
541
541
|
|
|
542
|
-
it(
|
|
543
|
-
const result = asVNode(Element({ children:
|
|
542
|
+
it('does not pass extendCss when css not provided', () => {
|
|
543
|
+
const result = asVNode(Element({ children: 'test' }))
|
|
544
544
|
expect(result.props.extendCss).toBeUndefined()
|
|
545
545
|
})
|
|
546
546
|
})
|
|
547
547
|
|
|
548
|
-
describe(
|
|
549
|
-
it(
|
|
550
|
-
const result = asVNode(Element({ children:
|
|
548
|
+
describe('content fallback chain', () => {
|
|
549
|
+
it('prefers children over content', () => {
|
|
550
|
+
const result = asVNode(Element({ children: 'child', content: 'alt' }))
|
|
551
551
|
const children = result.props.children as unknown[]
|
|
552
552
|
expect(children).toBeDefined()
|
|
553
553
|
expect(Array.isArray(children)).toBe(true)
|
|
554
554
|
})
|
|
555
555
|
|
|
556
|
-
it(
|
|
557
|
-
const result = asVNode(Element({ content:
|
|
556
|
+
it('falls back to content when no children', () => {
|
|
557
|
+
const result = asVNode(Element({ content: 'alt content' }))
|
|
558
558
|
const children = result.props.children as unknown[]
|
|
559
559
|
expect(children).toBeDefined()
|
|
560
560
|
})
|
|
561
561
|
|
|
562
|
-
it(
|
|
563
|
-
const result = asVNode(Element({ label:
|
|
562
|
+
it('falls back to label when no children or content', () => {
|
|
563
|
+
const result = asVNode(Element({ label: 'label text' }))
|
|
564
564
|
const children = result.props.children as unknown[]
|
|
565
565
|
expect(children).toBeDefined()
|
|
566
566
|
})
|
|
567
567
|
})
|
|
568
568
|
|
|
569
|
-
describe(
|
|
570
|
-
it(
|
|
571
|
-
const result = asVNode(Element({ children:
|
|
569
|
+
describe('Wrapper as prop reset', () => {
|
|
570
|
+
it('resets the as prop to undefined on Wrapper', () => {
|
|
571
|
+
const result = asVNode(Element({ children: 'test' }))
|
|
572
572
|
expect(result.props.as).toBeUndefined()
|
|
573
573
|
})
|
|
574
574
|
})
|
|
575
575
|
|
|
576
|
-
describe(
|
|
577
|
-
it(
|
|
578
|
-
const result = asVNode(Element({ tag:
|
|
576
|
+
describe('button tag (flex fix needed)', () => {
|
|
577
|
+
it('passes tag as button to Wrapper', () => {
|
|
578
|
+
const result = asVNode(Element({ tag: 'button', children: 'click' }))
|
|
579
579
|
expect(result.type).toBe(Wrapper)
|
|
580
|
-
expect(result.props.tag).toBe(
|
|
580
|
+
expect(result.props.tag).toBe('button')
|
|
581
581
|
})
|
|
582
582
|
|
|
583
|
-
it(
|
|
584
|
-
const result = asVNode(Element({ tag:
|
|
583
|
+
it('passes isInline=true for button (inline element)', () => {
|
|
584
|
+
const result = asVNode(Element({ tag: 'button', children: 'click' }))
|
|
585
585
|
expect(result.props.isInline).toBe(true)
|
|
586
586
|
})
|
|
587
587
|
})
|
|
588
588
|
|
|
589
|
-
describe(
|
|
590
|
-
it(
|
|
589
|
+
describe('component metadata', () => {
|
|
590
|
+
it('has displayName set', () => {
|
|
591
591
|
expect(Element.displayName).toBeDefined()
|
|
592
|
-
expect(Element.displayName).toContain(
|
|
592
|
+
expect(Element.displayName).toContain('Element')
|
|
593
593
|
})
|
|
594
594
|
|
|
595
|
-
it(
|
|
595
|
+
it('has PYREON__COMPONENT set', () => {
|
|
596
596
|
expect(Element.PYREON__COMPONENT).toBeDefined()
|
|
597
|
-
expect(Element.PYREON__COMPONENT).toContain(
|
|
597
|
+
expect(Element.PYREON__COMPONENT).toContain('Element')
|
|
598
598
|
})
|
|
599
599
|
|
|
600
|
-
it(
|
|
600
|
+
it('has pkgName set', () => {
|
|
601
601
|
expect(Element.pkgName).toBeDefined()
|
|
602
602
|
})
|
|
603
603
|
})
|