@pyreon/elements 0.24.5 → 0.25.0

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 (71) hide show
  1. package/lib/index.js +5 -1
  2. package/package.json +11 -13
  3. package/src/Element/component.tsx +0 -315
  4. package/src/Element/constants.ts +0 -96
  5. package/src/Element/index.ts +0 -6
  6. package/src/Element/types.ts +0 -168
  7. package/src/Element/utils.ts +0 -15
  8. package/src/List/component.tsx +0 -105
  9. package/src/List/index.ts +0 -5
  10. package/src/Overlay/component.tsx +0 -140
  11. package/src/Overlay/context.tsx +0 -36
  12. package/src/Overlay/index.ts +0 -7
  13. package/src/Overlay/positioning.ts +0 -191
  14. package/src/Overlay/useOverlay.tsx +0 -461
  15. package/src/Portal/component.tsx +0 -54
  16. package/src/Portal/index.ts +0 -5
  17. package/src/Text/component.tsx +0 -67
  18. package/src/Text/index.ts +0 -5
  19. package/src/Text/styled.ts +0 -30
  20. package/src/Util/component.tsx +0 -43
  21. package/src/Util/index.ts +0 -5
  22. package/src/__tests__/Content.test.tsx +0 -123
  23. package/src/__tests__/Element-slot-reactivity.browser.test.tsx +0 -152
  24. package/src/__tests__/Element.test.ts +0 -819
  25. package/src/__tests__/Iterator.test.ts +0 -492
  26. package/src/__tests__/Iterator.types.test.ts +0 -237
  27. package/src/__tests__/List.test.ts +0 -199
  28. package/src/__tests__/Overlay.test.ts +0 -492
  29. package/src/__tests__/Portal.test.ts +0 -156
  30. package/src/__tests__/Text.test.ts +0 -274
  31. package/src/__tests__/Util.test.ts +0 -63
  32. package/src/__tests__/Wrapper-innerhtml.test.tsx +0 -178
  33. package/src/__tests__/Wrapper.test.tsx +0 -196
  34. package/src/__tests__/elements.browser.test.tsx +0 -132
  35. package/src/__tests__/equalBeforeAfter.test.ts +0 -122
  36. package/src/__tests__/helpers.test.ts +0 -65
  37. package/src/__tests__/integration.test.tsx +0 -118
  38. package/src/__tests__/internElementBundle.test.ts +0 -102
  39. package/src/__tests__/iterator-function-children.test.tsx +0 -120
  40. package/src/__tests__/native-markers.test.ts +0 -13
  41. package/src/__tests__/overlayContext.test.tsx +0 -78
  42. package/src/__tests__/perf-stress.browser.test.tsx +0 -119
  43. package/src/__tests__/positioning.test.ts +0 -90
  44. package/src/__tests__/responsiveProps.test.ts +0 -328
  45. package/src/__tests__/slot-component-reference.test.tsx +0 -157
  46. package/src/__tests__/useOverlay.test.ts +0 -1336
  47. package/src/__tests__/utils.test.ts +0 -69
  48. package/src/__tests__/wrapper-block-cascade.test.ts +0 -121
  49. package/src/constants.ts +0 -1
  50. package/src/env.d.ts +0 -6
  51. package/src/helpers/Content/component.tsx +0 -75
  52. package/src/helpers/Content/index.ts +0 -3
  53. package/src/helpers/Content/styled.ts +0 -105
  54. package/src/helpers/Content/types.ts +0 -49
  55. package/src/helpers/Iterator/component.tsx +0 -316
  56. package/src/helpers/Iterator/index.ts +0 -30
  57. package/src/helpers/Iterator/types.ts +0 -138
  58. package/src/helpers/Wrapper/component.tsx +0 -180
  59. package/src/helpers/Wrapper/constants.ts +0 -10
  60. package/src/helpers/Wrapper/index.ts +0 -3
  61. package/src/helpers/Wrapper/styled.ts +0 -64
  62. package/src/helpers/Wrapper/types.ts +0 -56
  63. package/src/helpers/Wrapper/utils.ts +0 -7
  64. package/src/helpers/index.ts +0 -4
  65. package/src/helpers/internElementBundle.ts +0 -37
  66. package/src/helpers/isPyreonComponent.ts +0 -46
  67. package/src/index.ts +0 -42
  68. package/src/manifest.ts +0 -190
  69. package/src/tests/manifest-snapshot.test.ts +0 -45
  70. package/src/types.ts +0 -112
  71. 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
- })