@pyreon/elements 0.11.5 → 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,68 +1,68 @@
|
|
|
1
|
-
import type { ComponentFn, VNode, VNodeChild } from
|
|
2
|
-
import { Fragment, h } from
|
|
3
|
-
import { describe, expect, it, vi } from
|
|
4
|
-
import Iterator from
|
|
1
|
+
import type { ComponentFn, VNode, VNodeChild } from '@pyreon/core'
|
|
2
|
+
import { Fragment, h } from '@pyreon/core'
|
|
3
|
+
import { describe, expect, it, vi } from 'vitest'
|
|
4
|
+
import Iterator from '../helpers/Iterator/component'
|
|
5
5
|
|
|
6
6
|
const asVNode = (v: unknown) => v as VNode
|
|
7
7
|
|
|
8
8
|
const TextItem: ComponentFn = (props: any) =>
|
|
9
|
-
h(
|
|
9
|
+
h('span', { 'data-testid': 'item', ...props }, props.children)
|
|
10
10
|
|
|
11
|
-
describe(
|
|
12
|
-
describe(
|
|
13
|
-
it(
|
|
11
|
+
describe('Iterator', () => {
|
|
12
|
+
describe('static properties', () => {
|
|
13
|
+
it('has isIterator flag', () => {
|
|
14
14
|
expect(Iterator.isIterator).toBe(true)
|
|
15
15
|
})
|
|
16
16
|
|
|
17
|
-
it(
|
|
18
|
-
expect(Iterator.RESERVED_PROPS).toContain(
|
|
19
|
-
expect(Iterator.RESERVED_PROPS).toContain(
|
|
20
|
-
expect(Iterator.RESERVED_PROPS).toContain(
|
|
21
|
-
expect(Iterator.RESERVED_PROPS).toContain(
|
|
22
|
-
expect(Iterator.RESERVED_PROPS).toContain(
|
|
23
|
-
expect(Iterator.RESERVED_PROPS).toContain(
|
|
24
|
-
expect(Iterator.RESERVED_PROPS).toContain(
|
|
25
|
-
expect(Iterator.RESERVED_PROPS).toContain(
|
|
17
|
+
it('has RESERVED_PROPS', () => {
|
|
18
|
+
expect(Iterator.RESERVED_PROPS).toContain('children')
|
|
19
|
+
expect(Iterator.RESERVED_PROPS).toContain('component')
|
|
20
|
+
expect(Iterator.RESERVED_PROPS).toContain('data')
|
|
21
|
+
expect(Iterator.RESERVED_PROPS).toContain('itemKey')
|
|
22
|
+
expect(Iterator.RESERVED_PROPS).toContain('valueName')
|
|
23
|
+
expect(Iterator.RESERVED_PROPS).toContain('itemProps')
|
|
24
|
+
expect(Iterator.RESERVED_PROPS).toContain('wrapComponent')
|
|
25
|
+
expect(Iterator.RESERVED_PROPS).toContain('wrapProps')
|
|
26
26
|
})
|
|
27
27
|
})
|
|
28
28
|
|
|
29
|
-
describe(
|
|
30
|
-
it(
|
|
29
|
+
describe('children mode', () => {
|
|
30
|
+
it('renders children directly', () => {
|
|
31
31
|
const children = [
|
|
32
|
-
h(
|
|
33
|
-
h(
|
|
32
|
+
h('span', { 'data-testid': 'child-1' }, 'A'),
|
|
33
|
+
h('span', { 'data-testid': 'child-2' }, 'B'),
|
|
34
34
|
]
|
|
35
35
|
const result = Iterator({ children })
|
|
36
36
|
expect(result).toEqual(children)
|
|
37
37
|
})
|
|
38
38
|
|
|
39
|
-
it(
|
|
40
|
-
const child = h(
|
|
39
|
+
it('renders single child', () => {
|
|
40
|
+
const child = h('span', { 'data-testid': 'only' }, 'Only')
|
|
41
41
|
const result = Iterator({ children: child })
|
|
42
42
|
expect(result).toBe(child)
|
|
43
43
|
})
|
|
44
44
|
|
|
45
|
-
it(
|
|
45
|
+
it('returns null when children is null/undefined', () => {
|
|
46
46
|
const result = Iterator({})
|
|
47
47
|
expect(result).toBeNull()
|
|
48
48
|
})
|
|
49
49
|
|
|
50
|
-
it(
|
|
50
|
+
it('renders fragment children', () => {
|
|
51
51
|
const children = [
|
|
52
|
-
h(
|
|
53
|
-
h(
|
|
52
|
+
h('span', { 'data-testid': 'frag-1' }, 'A'),
|
|
53
|
+
h('span', { 'data-testid': 'frag-2' }, 'B'),
|
|
54
54
|
]
|
|
55
55
|
const result = Iterator({ children })
|
|
56
56
|
expect(result).toEqual(children)
|
|
57
57
|
})
|
|
58
58
|
|
|
59
|
-
it(
|
|
59
|
+
it('renders Fragment children with itemProps', () => {
|
|
60
60
|
const itemPropsFn = vi.fn((_item: unknown, extended: any) => ({
|
|
61
|
-
|
|
61
|
+
'data-pos': String(extended.position),
|
|
62
62
|
}))
|
|
63
63
|
const fragChildren = [
|
|
64
|
-
h(
|
|
65
|
-
h(
|
|
64
|
+
h('span', { 'data-testid': 'frag-a' }, 'A'),
|
|
65
|
+
h('span', { 'data-testid': 'frag-b' }, 'B'),
|
|
66
66
|
]
|
|
67
67
|
const fragment = h(Fragment, null, ...fragChildren)
|
|
68
68
|
const result = Iterator({ children: fragment, itemProps: itemPropsFn })
|
|
@@ -70,11 +70,11 @@ describe("Iterator", () => {
|
|
|
70
70
|
expect(Array.isArray(result)).toBe(true)
|
|
71
71
|
})
|
|
72
72
|
|
|
73
|
-
it(
|
|
74
|
-
const Wrap: ComponentFn = (props: any) => h(
|
|
73
|
+
it('renders Fragment children with wrapComponent', () => {
|
|
74
|
+
const Wrap: ComponentFn = (props: any) => h('div', { 'data-testid': 'wrap' }, props.children)
|
|
75
75
|
const fragChildren = [
|
|
76
|
-
h(
|
|
77
|
-
h(
|
|
76
|
+
h('span', { 'data-testid': 'frag-a' }, 'A'),
|
|
77
|
+
h('span', { 'data-testid': 'frag-b' }, 'B'),
|
|
78
78
|
]
|
|
79
79
|
const fragment = h(Fragment, null, ...fragChildren)
|
|
80
80
|
const result = Iterator({ children: fragment, wrapComponent: Wrap }) as VNodeChild[]
|
|
@@ -85,28 +85,28 @@ describe("Iterator", () => {
|
|
|
85
85
|
expect(first.type).toBe(Wrap)
|
|
86
86
|
})
|
|
87
87
|
|
|
88
|
-
it(
|
|
89
|
-
const child = h(
|
|
88
|
+
it('children take priority over data', () => {
|
|
89
|
+
const child = h('span', { 'data-testid': 'child' }, 'Child wins')
|
|
90
90
|
const result = Iterator({
|
|
91
91
|
children: child,
|
|
92
92
|
component: TextItem,
|
|
93
|
-
data: [
|
|
93
|
+
data: ['x', 'y'],
|
|
94
94
|
})
|
|
95
95
|
expect(result).toBe(child)
|
|
96
96
|
})
|
|
97
97
|
})
|
|
98
98
|
|
|
99
|
-
describe(
|
|
100
|
-
it(
|
|
99
|
+
describe('simple array mode', () => {
|
|
100
|
+
it('renders string array with component', () => {
|
|
101
101
|
const result = Iterator({
|
|
102
102
|
component: TextItem,
|
|
103
|
-
data: [
|
|
103
|
+
data: ['hello', 'world'],
|
|
104
104
|
}) as VNodeChild[]
|
|
105
105
|
expect(Array.isArray(result)).toBe(true)
|
|
106
106
|
expect(result).toHaveLength(2)
|
|
107
107
|
})
|
|
108
108
|
|
|
109
|
-
it(
|
|
109
|
+
it('renders number array with component', () => {
|
|
110
110
|
const result = Iterator({
|
|
111
111
|
component: TextItem,
|
|
112
112
|
data: [1, 2, 3],
|
|
@@ -115,77 +115,77 @@ describe("Iterator", () => {
|
|
|
115
115
|
expect(result).toHaveLength(3)
|
|
116
116
|
})
|
|
117
117
|
|
|
118
|
-
it(
|
|
118
|
+
it('filters null/undefined from data', () => {
|
|
119
119
|
const result = Iterator({
|
|
120
120
|
component: TextItem,
|
|
121
|
-
data: [
|
|
121
|
+
data: ['a', null, 'b', undefined],
|
|
122
122
|
}) as VNodeChild[]
|
|
123
123
|
expect(Array.isArray(result)).toBe(true)
|
|
124
124
|
expect(result).toHaveLength(2)
|
|
125
125
|
})
|
|
126
126
|
|
|
127
|
-
it(
|
|
127
|
+
it('returns null for empty array', () => {
|
|
128
128
|
const result = Iterator({ component: TextItem, data: [] })
|
|
129
129
|
expect(result).toBeNull()
|
|
130
130
|
})
|
|
131
131
|
|
|
132
|
-
it(
|
|
132
|
+
it('returns null for all-null array', () => {
|
|
133
133
|
const result = Iterator({ component: TextItem, data: [null, null] })
|
|
134
134
|
expect(result).toBeNull()
|
|
135
135
|
})
|
|
136
136
|
|
|
137
|
-
it(
|
|
138
|
-
const Item: ComponentFn = (props: any) => h(
|
|
137
|
+
it('uses valueName to set prop name', () => {
|
|
138
|
+
const Item: ComponentFn = (props: any) => h('span', { 'data-testid': 'item' }, props.title)
|
|
139
139
|
const result = Iterator({
|
|
140
140
|
component: Item,
|
|
141
|
-
data: [
|
|
142
|
-
valueName:
|
|
141
|
+
data: ['hello'],
|
|
142
|
+
valueName: 'title',
|
|
143
143
|
}) as VNodeChild[]
|
|
144
144
|
expect(Array.isArray(result)).toBe(true)
|
|
145
145
|
expect(result).toHaveLength(1)
|
|
146
146
|
})
|
|
147
147
|
|
|
148
|
-
it(
|
|
148
|
+
it('defaults valueName to children', () => {
|
|
149
149
|
const result = Iterator({
|
|
150
150
|
component: TextItem,
|
|
151
|
-
data: [
|
|
151
|
+
data: ['test'],
|
|
152
152
|
}) as VNodeChild[]
|
|
153
153
|
expect(Array.isArray(result)).toBe(true)
|
|
154
154
|
expect(result).toHaveLength(1)
|
|
155
155
|
})
|
|
156
156
|
})
|
|
157
157
|
|
|
158
|
-
describe(
|
|
159
|
-
it(
|
|
160
|
-
const Item: ComponentFn = (props: any) => h(
|
|
158
|
+
describe('object array mode', () => {
|
|
159
|
+
it('renders object array with component', () => {
|
|
160
|
+
const Item: ComponentFn = (props: any) => h('span', { 'data-testid': 'item' }, props.name)
|
|
161
161
|
const result = Iterator({
|
|
162
162
|
component: Item,
|
|
163
163
|
data: [
|
|
164
|
-
{ id: 1, name:
|
|
165
|
-
{ id: 2, name:
|
|
164
|
+
{ id: 1, name: 'Alice' },
|
|
165
|
+
{ id: 2, name: 'Bob' },
|
|
166
166
|
],
|
|
167
167
|
}) as VNodeChild[]
|
|
168
168
|
expect(Array.isArray(result)).toBe(true)
|
|
169
169
|
expect(result).toHaveLength(2)
|
|
170
170
|
})
|
|
171
171
|
|
|
172
|
-
it(
|
|
173
|
-
const Item: ComponentFn = (props: any) => h(
|
|
172
|
+
it('filters empty objects from data', () => {
|
|
173
|
+
const Item: ComponentFn = (props: any) => h('span', { 'data-testid': 'item' }, props.name)
|
|
174
174
|
const result = Iterator({
|
|
175
175
|
component: Item,
|
|
176
|
-
data: [{ name:
|
|
176
|
+
data: [{ name: 'Alice' }, {}, { name: 'Bob' }],
|
|
177
177
|
}) as VNodeChild[]
|
|
178
178
|
expect(Array.isArray(result)).toBe(true)
|
|
179
179
|
expect(result).toHaveLength(2)
|
|
180
180
|
})
|
|
181
181
|
|
|
182
|
-
it(
|
|
182
|
+
it('supports per-item component override', () => {
|
|
183
183
|
const Default: ComponentFn = (props: any) =>
|
|
184
|
-
h(
|
|
185
|
-
const Custom: ComponentFn = (props: any) => h(
|
|
184
|
+
h('span', { 'data-testid': 'default' }, props.label)
|
|
185
|
+
const Custom: ComponentFn = (props: any) => h('em', { 'data-testid': 'custom' }, props.label)
|
|
186
186
|
const result = Iterator({
|
|
187
187
|
component: Default,
|
|
188
|
-
data: [{ label:
|
|
188
|
+
data: [{ label: 'one' }, { label: 'two', component: Custom }],
|
|
189
189
|
}) as VNodeChild[]
|
|
190
190
|
expect(Array.isArray(result)).toBe(true)
|
|
191
191
|
expect(result).toHaveLength(2)
|
|
@@ -194,53 +194,53 @@ describe("Iterator", () => {
|
|
|
194
194
|
expect(second.type).toBe(Custom)
|
|
195
195
|
})
|
|
196
196
|
|
|
197
|
-
it(
|
|
198
|
-
const Item: ComponentFn = (props: any) => h(
|
|
197
|
+
it('uses itemKey string to pick key from item', () => {
|
|
198
|
+
const Item: ComponentFn = (props: any) => h('span', { 'data-testid': 'item' }, props.slug)
|
|
199
199
|
const result = Iterator({
|
|
200
200
|
component: Item,
|
|
201
|
-
data: [{ slug:
|
|
202
|
-
itemKey:
|
|
201
|
+
data: [{ slug: 'a' }, { slug: 'b' }],
|
|
202
|
+
itemKey: 'slug',
|
|
203
203
|
}) as VNodeChild[]
|
|
204
204
|
expect(Array.isArray(result)).toBe(true)
|
|
205
205
|
expect(result).toHaveLength(2)
|
|
206
206
|
})
|
|
207
207
|
|
|
208
|
-
it(
|
|
208
|
+
it('uses itemKey function for custom keys', () => {
|
|
209
209
|
const keyFn = vi.fn((_item: unknown, index: number) => `custom-${index}`)
|
|
210
|
-
const Item: ComponentFn = (props: any) => h(
|
|
210
|
+
const Item: ComponentFn = (props: any) => h('span', { 'data-testid': 'item' }, props.name)
|
|
211
211
|
Iterator({
|
|
212
212
|
component: Item,
|
|
213
|
-
data: [{ name:
|
|
213
|
+
data: [{ name: 'a' }, { name: 'b' }],
|
|
214
214
|
itemKey: keyFn,
|
|
215
215
|
})
|
|
216
216
|
expect(keyFn).toHaveBeenCalledTimes(2)
|
|
217
217
|
})
|
|
218
218
|
|
|
219
|
-
it(
|
|
220
|
-
const Item: ComponentFn = (props: any) => h(
|
|
219
|
+
it('falls back to id/key/itemId for keys', () => {
|
|
220
|
+
const Item: ComponentFn = (props: any) => h('span', { 'data-testid': 'item' }, props.name)
|
|
221
221
|
const result = Iterator({
|
|
222
222
|
component: Item,
|
|
223
223
|
data: [
|
|
224
|
-
{ id:
|
|
225
|
-
{ key:
|
|
226
|
-
{ itemId:
|
|
224
|
+
{ id: 'x', name: 'Alice' },
|
|
225
|
+
{ key: 'y', name: 'Bob' },
|
|
226
|
+
{ itemId: 'z', name: 'Charlie' },
|
|
227
227
|
],
|
|
228
228
|
}) as VNodeChild[]
|
|
229
229
|
expect(result).toHaveLength(3)
|
|
230
230
|
})
|
|
231
231
|
})
|
|
232
232
|
|
|
233
|
-
describe(
|
|
234
|
-
it(
|
|
233
|
+
describe('itemProps', () => {
|
|
234
|
+
it('passes static itemProps to items', () => {
|
|
235
235
|
const result = Iterator({
|
|
236
236
|
component: TextItem,
|
|
237
|
-
data: [
|
|
238
|
-
itemProps: { extra:
|
|
237
|
+
data: ['hello'],
|
|
238
|
+
itemProps: { extra: 'yes' },
|
|
239
239
|
}) as VNodeChild[]
|
|
240
240
|
expect(result).toHaveLength(1)
|
|
241
241
|
})
|
|
242
242
|
|
|
243
|
-
it(
|
|
243
|
+
it('passes itemProps callback with extended props', () => {
|
|
244
244
|
const itemPropsFn = vi.fn((_item: unknown, extended: any) => ({
|
|
245
245
|
pos: extended.position,
|
|
246
246
|
isFirst: extended.first,
|
|
@@ -248,7 +248,7 @@ describe("Iterator", () => {
|
|
|
248
248
|
}))
|
|
249
249
|
Iterator({
|
|
250
250
|
component: TextItem,
|
|
251
|
-
data: [
|
|
251
|
+
data: ['a', 'b', 'c'],
|
|
252
252
|
itemProps: itemPropsFn,
|
|
253
253
|
})
|
|
254
254
|
expect(itemPropsFn).toHaveBeenCalledTimes(3)
|
|
@@ -268,12 +268,12 @@ describe("Iterator", () => {
|
|
|
268
268
|
})
|
|
269
269
|
})
|
|
270
270
|
|
|
271
|
-
describe(
|
|
272
|
-
it(
|
|
273
|
-
const Wrap: ComponentFn = (props: any) => h(
|
|
271
|
+
describe('wrapComponent', () => {
|
|
272
|
+
it('wraps each item with wrapComponent', () => {
|
|
273
|
+
const Wrap: ComponentFn = (props: any) => h('div', { 'data-testid': 'wrap' }, props.children)
|
|
274
274
|
const result = Iterator({
|
|
275
275
|
component: TextItem,
|
|
276
|
-
data: [
|
|
276
|
+
data: ['a', 'b'],
|
|
277
277
|
wrapComponent: Wrap,
|
|
278
278
|
}) as VNodeChild[]
|
|
279
279
|
expect(result).toHaveLength(2)
|
|
@@ -281,41 +281,41 @@ describe("Iterator", () => {
|
|
|
281
281
|
expect(first.type).toBe(Wrap)
|
|
282
282
|
})
|
|
283
283
|
|
|
284
|
-
it(
|
|
285
|
-
const Wrap: ComponentFn = (props: any) => h(
|
|
284
|
+
it('wraps children with wrapComponent', () => {
|
|
285
|
+
const Wrap: ComponentFn = (props: any) => h('div', { 'data-testid': 'wrap' }, props.children)
|
|
286
286
|
const result = Iterator({
|
|
287
287
|
wrapComponent: Wrap,
|
|
288
|
-
children: [h(
|
|
288
|
+
children: [h('span', null, 'A'), h('span', null, 'B')],
|
|
289
289
|
}) as VNodeChild[]
|
|
290
290
|
expect(result).toHaveLength(2)
|
|
291
291
|
const first = asVNode(result[0])
|
|
292
292
|
expect(first.type).toBe(Wrap)
|
|
293
293
|
})
|
|
294
294
|
|
|
295
|
-
it(
|
|
295
|
+
it('passes wrapProps to wrapComponent', () => {
|
|
296
296
|
const Wrap: ComponentFn = (props: any) =>
|
|
297
|
-
h(
|
|
297
|
+
h('div', { 'data-testid': 'wrap', 'data-extra': props.extra }, props.children)
|
|
298
298
|
const result = Iterator({
|
|
299
299
|
component: TextItem,
|
|
300
|
-
data: [
|
|
300
|
+
data: ['a'],
|
|
301
301
|
wrapComponent: Wrap,
|
|
302
|
-
wrapProps: { extra:
|
|
302
|
+
wrapProps: { extra: 'val' },
|
|
303
303
|
}) as VNodeChild[]
|
|
304
304
|
expect(result).toHaveLength(1)
|
|
305
305
|
const first = asVNode(result[0])
|
|
306
306
|
expect(first.type).toBe(Wrap)
|
|
307
|
-
expect(first.props.extra).toBe(
|
|
307
|
+
expect(first.props.extra).toBe('val')
|
|
308
308
|
})
|
|
309
309
|
|
|
310
|
-
it(
|
|
310
|
+
it('passes wrapProps callback with extended props', () => {
|
|
311
311
|
const wrapPropsFn = vi.fn((_item: unknown, extended: any) => ({
|
|
312
|
-
|
|
312
|
+
'data-pos': extended.position,
|
|
313
313
|
}))
|
|
314
314
|
const Wrap: ComponentFn = (props: any) =>
|
|
315
|
-
h(
|
|
315
|
+
h('div', { 'data-testid': 'wrap', ...props }, props.children)
|
|
316
316
|
Iterator({
|
|
317
317
|
component: TextItem,
|
|
318
|
-
data: [
|
|
318
|
+
data: ['a', 'b'],
|
|
319
319
|
wrapComponent: Wrap,
|
|
320
320
|
wrapProps: wrapPropsFn,
|
|
321
321
|
})
|
|
@@ -325,16 +325,16 @@ describe("Iterator", () => {
|
|
|
325
325
|
expect((wCalls[1] as unknown[])[1]).toMatchObject({ position: 2 })
|
|
326
326
|
})
|
|
327
327
|
|
|
328
|
-
it(
|
|
328
|
+
it('wraps object array items with wrapComponent and wrapProps callback', () => {
|
|
329
329
|
const wrapPropsFn = vi.fn((_item: unknown, extended: any) => ({
|
|
330
|
-
|
|
330
|
+
'data-pos': String(extended.position),
|
|
331
331
|
}))
|
|
332
|
-
const Item: ComponentFn = (props: any) => h(
|
|
332
|
+
const Item: ComponentFn = (props: any) => h('span', { 'data-testid': 'item' }, props.name)
|
|
333
333
|
const Wrap: ComponentFn = (props: any) =>
|
|
334
|
-
h(
|
|
334
|
+
h('div', { 'data-testid': 'wrap', ...props }, props.children)
|
|
335
335
|
const result = Iterator({
|
|
336
336
|
component: Item,
|
|
337
|
-
data: [{ name:
|
|
337
|
+
data: [{ name: 'Alice' }, { name: 'Bob' }],
|
|
338
338
|
wrapComponent: Wrap,
|
|
339
339
|
wrapProps: wrapPropsFn,
|
|
340
340
|
}) as VNodeChild[]
|
|
@@ -342,15 +342,15 @@ describe("Iterator", () => {
|
|
|
342
342
|
expect(wrapPropsFn).toHaveBeenCalledTimes(2)
|
|
343
343
|
})
|
|
344
344
|
|
|
345
|
-
it(
|
|
345
|
+
it('passes itemProps callback to object array items', () => {
|
|
346
346
|
const itemPropsFn = vi.fn((_item: unknown, extended: any) => ({
|
|
347
|
-
|
|
347
|
+
'data-first': String(extended.first),
|
|
348
348
|
}))
|
|
349
349
|
const Item: ComponentFn = (props: any) =>
|
|
350
|
-
h(
|
|
350
|
+
h('span', { 'data-testid': 'item', ...props }, props.name)
|
|
351
351
|
Iterator({
|
|
352
352
|
component: Item,
|
|
353
|
-
data: [{ name:
|
|
353
|
+
data: [{ name: 'Alice' }, { name: 'Bob' }],
|
|
354
354
|
itemProps: itemPropsFn,
|
|
355
355
|
})
|
|
356
356
|
expect(itemPropsFn).toHaveBeenCalledTimes(2)
|
|
@@ -359,14 +359,14 @@ describe("Iterator", () => {
|
|
|
359
359
|
expect((ipCalls[1] as unknown[])[1]).toMatchObject({ first: false })
|
|
360
360
|
})
|
|
361
361
|
|
|
362
|
-
it(
|
|
362
|
+
it('skips wrapComponent for items with custom component in object array', () => {
|
|
363
363
|
const Default: ComponentFn = (props: any) =>
|
|
364
|
-
h(
|
|
365
|
-
const Custom: ComponentFn = (props: any) => h(
|
|
366
|
-
const Wrap: ComponentFn = (props: any) => h(
|
|
364
|
+
h('span', { 'data-testid': 'default' }, props.label)
|
|
365
|
+
const Custom: ComponentFn = (props: any) => h('em', { 'data-testid': 'custom' }, props.label)
|
|
366
|
+
const Wrap: ComponentFn = (props: any) => h('div', { 'data-testid': 'wrap' }, props.children)
|
|
367
367
|
const result = Iterator({
|
|
368
368
|
component: Default,
|
|
369
|
-
data: [{ label:
|
|
369
|
+
data: [{ label: 'one' }, { label: 'two', component: Custom }],
|
|
370
370
|
wrapComponent: Wrap,
|
|
371
371
|
}) as VNodeChild[]
|
|
372
372
|
expect(result).toHaveLength(2)
|
|
@@ -379,90 +379,90 @@ describe("Iterator", () => {
|
|
|
379
379
|
})
|
|
380
380
|
})
|
|
381
381
|
|
|
382
|
-
describe(
|
|
383
|
-
it(
|
|
384
|
-
const itemPropsFn = vi.fn(() => ({
|
|
382
|
+
describe('children with itemProps (no wrapComponent)', () => {
|
|
383
|
+
it('injects itemProps into children without wrapping', () => {
|
|
384
|
+
const itemPropsFn = vi.fn(() => ({ 'data-injected': 'yes' }))
|
|
385
385
|
Iterator({
|
|
386
386
|
itemProps: itemPropsFn,
|
|
387
387
|
children: [
|
|
388
|
-
h(
|
|
389
|
-
h(
|
|
388
|
+
h('span', { 'data-testid': 'child-a' }, 'A'),
|
|
389
|
+
h('span', { 'data-testid': 'child-b' }, 'B'),
|
|
390
390
|
],
|
|
391
391
|
})
|
|
392
392
|
expect(itemPropsFn).toHaveBeenCalled()
|
|
393
393
|
})
|
|
394
394
|
|
|
395
|
-
it(
|
|
395
|
+
it('injects itemProps into single child', () => {
|
|
396
396
|
const itemPropsFn = vi.fn(() => ({}))
|
|
397
397
|
Iterator({
|
|
398
398
|
itemProps: itemPropsFn,
|
|
399
|
-
children: h(
|
|
399
|
+
children: h('span', { 'data-testid': 'only' }, 'Only'),
|
|
400
400
|
})
|
|
401
401
|
expect(itemPropsFn).toHaveBeenCalled()
|
|
402
402
|
})
|
|
403
403
|
})
|
|
404
404
|
|
|
405
|
-
describe(
|
|
406
|
-
it(
|
|
407
|
-
const child = h(
|
|
405
|
+
describe('children rendering paths', () => {
|
|
406
|
+
it('renders single child without itemProps or wrapComponent (direct passthrough)', () => {
|
|
407
|
+
const child = h('span', { 'data-testid': 'single' }, 'Single')
|
|
408
408
|
const result = Iterator({ children: child })
|
|
409
409
|
expect(result).toBe(child)
|
|
410
410
|
})
|
|
411
411
|
|
|
412
|
-
it(
|
|
412
|
+
it('renders array children without itemProps or wrapComponent', () => {
|
|
413
413
|
const children = [
|
|
414
|
-
h(
|
|
415
|
-
h(
|
|
414
|
+
h('span', { 'data-testid': 'a' }, 'A'),
|
|
415
|
+
h('span', { 'data-testid': 'b' }, 'B'),
|
|
416
416
|
]
|
|
417
417
|
const result = Iterator({ children })
|
|
418
418
|
expect(result).toEqual(children)
|
|
419
419
|
})
|
|
420
420
|
|
|
421
|
-
it(
|
|
422
|
-
const Wrap: ComponentFn = (props: any) => h(
|
|
421
|
+
it('renders single child with wrapComponent', () => {
|
|
422
|
+
const Wrap: ComponentFn = (props: any) => h('div', { 'data-testid': 'wrap' }, props.children)
|
|
423
423
|
const result = Iterator({
|
|
424
424
|
wrapComponent: Wrap,
|
|
425
|
-
children: h(
|
|
425
|
+
children: h('span', { 'data-testid': 'only' }, 'Only'),
|
|
426
426
|
})
|
|
427
427
|
const vnode = asVNode(result)
|
|
428
428
|
expect(vnode.type).toBe(Wrap)
|
|
429
429
|
})
|
|
430
430
|
|
|
431
|
-
it(
|
|
431
|
+
it('renders single child with itemProps function', () => {
|
|
432
432
|
const itemPropsFn = vi.fn((_item: unknown, extended: any) => ({
|
|
433
|
-
|
|
433
|
+
'data-pos': String(extended.position),
|
|
434
434
|
}))
|
|
435
435
|
Iterator({
|
|
436
436
|
itemProps: itemPropsFn,
|
|
437
|
-
children: h(
|
|
437
|
+
children: h('span', { 'data-testid': 'only' }, 'Only'),
|
|
438
438
|
})
|
|
439
439
|
expect(itemPropsFn).toHaveBeenCalledTimes(1)
|
|
440
440
|
})
|
|
441
441
|
})
|
|
442
442
|
|
|
443
|
-
describe(
|
|
444
|
-
it(
|
|
445
|
-
const result = Iterator({ data: [
|
|
443
|
+
describe('edge cases', () => {
|
|
444
|
+
it('returns null when component is missing but data exists', () => {
|
|
445
|
+
const result = Iterator({ data: ['a', 'b'] })
|
|
446
446
|
expect(result).toBeNull()
|
|
447
447
|
})
|
|
448
448
|
|
|
449
|
-
it(
|
|
449
|
+
it('returns null when data is not an array', () => {
|
|
450
450
|
const result = Iterator({
|
|
451
451
|
component: TextItem,
|
|
452
|
-
data:
|
|
452
|
+
data: 'not-array' as any,
|
|
453
453
|
})
|
|
454
454
|
expect(result).toBeNull()
|
|
455
455
|
})
|
|
456
456
|
|
|
457
|
-
it(
|
|
457
|
+
it('returns null for mixed simple and object array', () => {
|
|
458
458
|
const result = Iterator({
|
|
459
459
|
component: TextItem,
|
|
460
|
-
data: [
|
|
460
|
+
data: ['hello', { name: 'world' }] as any,
|
|
461
461
|
})
|
|
462
462
|
expect(result).toBeNull()
|
|
463
463
|
})
|
|
464
464
|
|
|
465
|
-
it(
|
|
465
|
+
it('returns null for unsupported data types in array', () => {
|
|
466
466
|
const result = Iterator({
|
|
467
467
|
component: TextItem,
|
|
468
468
|
data: [true, false] as any,
|
|
@@ -470,11 +470,11 @@ describe("Iterator", () => {
|
|
|
470
470
|
expect(result).toBeNull()
|
|
471
471
|
})
|
|
472
472
|
|
|
473
|
-
it(
|
|
474
|
-
const Item: ComponentFn = (props: any) => h(
|
|
473
|
+
it('handles itemKey as number (fallback to index)', () => {
|
|
474
|
+
const Item: ComponentFn = (props: any) => h('span', { 'data-testid': 'item' }, props.name)
|
|
475
475
|
const result = Iterator({
|
|
476
476
|
component: Item,
|
|
477
|
-
data: [{ name:
|
|
477
|
+
data: [{ name: 'Alice' }, { name: 'Bob' }],
|
|
478
478
|
itemKey: 42 as any,
|
|
479
479
|
}) as VNodeChild[]
|
|
480
480
|
expect(result).toHaveLength(2)
|