@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,123 +0,0 @@
1
- import type { VNode } from '@pyreon/core'
2
- import { beforeEach, describe, expect, it, vi } from 'vitest'
3
-
4
- // ---------------------------------------------------------------------------
5
- // Mocks
6
- // ---------------------------------------------------------------------------
7
- const mocks = vi.hoisted(() => ({
8
- render: vi.fn((children: unknown) => children),
9
- }))
10
-
11
- vi.mock('@pyreon/ui-core', async (importOriginal) => {
12
- const actual = (await importOriginal()) as Record<string, unknown>
13
- return {
14
- ...actual,
15
- render: mocks.render,
16
- }
17
- })
18
-
19
- vi.mock('~/utils', () => ({
20
- IS_DEVELOPMENT: true,
21
- }))
22
-
23
- import Content from '../helpers/Content/component'
24
- import Styled from '../helpers/Content/styled'
25
-
26
- const asVNode = (v: unknown) => v as VNode
27
-
28
- describe('Content component', () => {
29
- beforeEach(() => {
30
- vi.clearAllMocks()
31
- })
32
-
33
- it('returns a VNode whose type is the Styled component', () => {
34
- const result = asVNode(Content({ contentType: 'content' }))
35
- expect(result.type).toBe(Styled)
36
- })
37
-
38
- it("passes tag as the 'as' prop to Styled", () => {
39
- const result = asVNode(Content({ tag: 'span' }))
40
- expect(result.props.as).toBe('span')
41
- })
42
-
43
- it('passes contentType as $contentType prop', () => {
44
- const result = asVNode(Content({ contentType: 'before' }))
45
- expect(result.props.$contentType).toBe('before')
46
- })
47
-
48
- it('passes styling props bundled as $element prop', () => {
49
- const result = asVNode(
50
- Content({
51
- contentType: 'content',
52
- parentDirection: 'inline',
53
- direction: 'rows',
54
- alignX: 'center',
55
- alignY: 'top',
56
- equalCols: true,
57
- gap: 8,
58
- extendCss: 'color: red;',
59
- }),
60
- )
61
-
62
- expect(result.props.$element).toEqual({
63
- contentType: 'content',
64
- parentDirection: 'inline',
65
- direction: 'rows',
66
- alignX: 'center',
67
- alignY: 'top',
68
- equalCols: true,
69
- gap: 8,
70
- extraStyles: 'color: red;',
71
- })
72
- })
73
-
74
- it('adds data-pyr-element attribute in development mode', () => {
75
- const result = asVNode(Content({ contentType: 'after' }))
76
- expect(result.props['data-pyr-element']).toBe('after')
77
- })
78
-
79
- it('passes children through render() when the slot accessor is invoked', () => {
80
- // Content wraps its children in a reactive accessor `() => resolveSlot(...)`
81
- // — render() is no longer called synchronously at component setup. The
82
- // accessor is invoked by the runtime when the JSX child position mounts;
83
- // here we invoke it directly to assert the wiring. The
84
- // function-unwrap-then-render shape is what keeps `content={() => <X/>}`
85
- // slot reactivity working (see Element-slot-reactivity.browser.test.tsx).
86
- const children = 'Some text'
87
- const result = asVNode(Content({ children }))
88
- expect(typeof result.props.children).toBe('function')
89
- ;(result.props.children as () => unknown)()
90
- expect(mocks.render).toHaveBeenCalledWith(children)
91
- })
92
-
93
- it('spreads remaining props to Styled', () => {
94
- const result = asVNode(Content({ id: 'test-id', className: 'custom' } as any))
95
- expect(result.props.id).toBe('test-id')
96
- expect(result.props.className).toBe('custom')
97
- })
98
-
99
- it('maps extendCss to extraStyles in $element', () => {
100
- const result = asVNode(Content({ extendCss: 'font-size: 14px;' }))
101
- expect((result.props.$element as any).extraStyles).toBe('font-size: 14px;')
102
- })
103
- })
104
-
105
- describe('Content component (production mode)', () => {
106
- it('does not add data-pyr-element when IS_DEVELOPMENT is false', async () => {
107
- // Reset module registry so the new mock takes effect
108
- vi.resetModules()
109
- vi.doMock('../utils', () => ({
110
- IS_DEVELOPMENT: false,
111
- }))
112
- // Re-mock @pyreon/ui-core after resetModules
113
- vi.doMock('@pyreon/ui-core', async (importOriginal) => {
114
- const actual = (await importOriginal()) as Record<string, unknown>
115
- return { ...actual, render: (children: unknown) => children }
116
- })
117
-
118
- const { default: ContentProd } = await import('../helpers/Content/component')
119
- const result = asVNode(ContentProd({ contentType: 'before' }))
120
-
121
- expect(result.props['data-pyr-element']).toBeUndefined()
122
- })
123
- })
@@ -1,152 +0,0 @@
1
- /** @jsxImportSource @pyreon/core */
2
- /**
3
- * Regression specs for the Element slot reactivity bug.
4
- *
5
- * Pre-fix: `<Element content={() => <Icon name={signal()} />}>` evaluated the
6
- * function once at mount and baked in the result. Signal changes inside the
7
- * function body did NOT cause the slot to re-render — even though the
8
- * `getChildren` helper in Element/component.tsx had a getter shape intended
9
- * to preserve reactivity.
10
- *
11
- * Root cause: the JSX child position read the resolved slot value at
12
- * component-setup time. The runtime's `mountChild` reactive-function-child
13
- * handling (`mountReactive`) was never reached because the function was
14
- * passed to `render()` which treated it as a component (one-shot mount),
15
- * not as a reactive accessor.
16
- *
17
- * Fix: wrap the JSX child position in `{() => ...}` so it becomes a
18
- * reactive accessor that mountChild routes through mountReactive.
19
- * Slot values that are themselves functions get unwrapped (called) inside
20
- * the accessor so their body's signal reads are tracked by the effect.
21
- *
22
- * Bisect-verify-with-restore: revert the wrap → these tests fail with
23
- * stuck slot content; restore → tests pass.
24
- */
25
- import { describe, expect, it } from 'vitest'
26
- import { signal } from '@pyreon/reactivity'
27
- import { flush, mountInBrowser } from '@pyreon/test-utils/browser'
28
- import { Element } from '../Element'
29
-
30
- describe('Element slot reactivity — function-valued slot props', () => {
31
- it('content={() => <X />} re-renders when a signal inside the function body changes', async () => {
32
- const dark = signal(false)
33
- const { container, unmount } = mountInBrowser(
34
- <Element
35
- tag="div"
36
- data-id="root"
37
- content={() => <span data-id="icon">{dark() ? 'moon' : 'sun'}</span>}
38
- />,
39
- )
40
-
41
- expect(container.querySelector('[data-id="icon"]')?.textContent).toBe('sun')
42
- dark.set(true)
43
- await flush()
44
- expect(container.querySelector('[data-id="icon"]')?.textContent).toBe('moon')
45
- dark.set(false)
46
- await flush()
47
- expect(container.querySelector('[data-id="icon"]')?.textContent).toBe('sun')
48
- unmount()
49
- })
50
-
51
- it('beforeContent={() => <X />} re-renders when a signal inside changes', async () => {
52
- const count = signal(0)
53
- const { container, unmount } = mountInBrowser(
54
- <Element
55
- tag="div"
56
- data-id="root"
57
- beforeContent={() => <span data-id="badge">{`#${count()}`}</span>}
58
- content={<span data-id="main">main</span>}
59
- />,
60
- )
61
-
62
- expect(container.querySelector('[data-id="badge"]')?.textContent).toBe('#0')
63
- count.set(5)
64
- await flush()
65
- expect(container.querySelector('[data-id="badge"]')?.textContent).toBe('#5')
66
- unmount()
67
- })
68
-
69
- it('afterContent={() => <X />} re-renders when a signal inside changes', async () => {
70
- const tag = signal('draft')
71
- const { container, unmount } = mountInBrowser(
72
- <Element
73
- tag="div"
74
- data-id="root"
75
- content={<span data-id="main">main</span>}
76
- afterContent={() => <span data-id="status">{tag()}</span>}
77
- />,
78
- )
79
-
80
- expect(container.querySelector('[data-id="status"]')?.textContent).toBe('draft')
81
- tag.set('published')
82
- await flush()
83
- expect(container.querySelector('[data-id="status"]')?.textContent).toBe('published')
84
- unmount()
85
- })
86
-
87
- it('static VNode content (non-function) still works unchanged', () => {
88
- // Regression guard for the static path — no function unwrap should
89
- // happen here.
90
- const { container, unmount } = mountInBrowser(
91
- <Element tag="div" content={<span data-id="static">hello</span>} />,
92
- )
93
- expect(container.querySelector('[data-id="static"]')?.textContent).toBe('hello')
94
- unmount()
95
- })
96
-
97
- it('static beforeContent + afterContent still render (compound path)', () => {
98
- const { container, unmount } = mountInBrowser(
99
- <Element
100
- tag="div"
101
- beforeContent={<span data-id="b">before</span>}
102
- content={<span data-id="c">main</span>}
103
- afterContent={<span data-id="a">after</span>}
104
- />,
105
- )
106
- expect(container.querySelector('[data-id="b"]')?.textContent).toBe('before')
107
- expect(container.querySelector('[data-id="c"]')?.textContent).toBe('main')
108
- expect(container.querySelector('[data-id="a"]')?.textContent).toBe('after')
109
- unmount()
110
- })
111
-
112
- it('null slot stays unrendered; flipping a signal can introduce it', async () => {
113
- const show = signal(false)
114
- const { container, unmount } = mountInBrowser(
115
- <Element
116
- tag="div"
117
- data-id="root"
118
- content={() => (show() ? <span data-id="present">shown</span> : null)}
119
- />,
120
- )
121
- expect(container.querySelector('[data-id="present"]')).toBeNull()
122
- show.set(true)
123
- await flush()
124
- expect(container.querySelector('[data-id="present"]')?.textContent).toBe('shown')
125
- show.set(false)
126
- await flush()
127
- expect(container.querySelector('[data-id="present"]')).toBeNull()
128
- unmount()
129
- })
130
-
131
- it('children prop (priority over content) — function form is reactive in compound layout', async () => {
132
- // children takes priority over content per getChildren's `??` chain.
133
- // Compound layout (when beforeContent OR afterContent exists) routes
134
- // through a different render path than the simple-element fast path —
135
- // both must handle function children reactively.
136
- const text = signal('first')
137
- const { container, unmount } = mountInBrowser(
138
- <Element
139
- tag="div"
140
- beforeContent={<span data-id="b">b</span>}
141
- afterContent={<span data-id="a">a</span>}
142
- >
143
- {() => <span data-id="kid">{text()}</span>}
144
- </Element>,
145
- )
146
- expect(container.querySelector('[data-id="kid"]')?.textContent).toBe('first')
147
- text.set('second')
148
- await flush()
149
- expect(container.querySelector('[data-id="kid"]')?.textContent).toBe('second')
150
- unmount()
151
- })
152
- })