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