@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.
- package/package.json +10 -12
- package/src/Element/component.tsx +0 -315
- package/src/Element/constants.ts +0 -96
- package/src/Element/index.ts +0 -6
- package/src/Element/types.ts +0 -168
- package/src/Element/utils.ts +0 -15
- package/src/List/component.tsx +0 -105
- package/src/List/index.ts +0 -5
- package/src/Overlay/component.tsx +0 -140
- package/src/Overlay/context.tsx +0 -36
- package/src/Overlay/index.ts +0 -7
- package/src/Overlay/positioning.ts +0 -191
- package/src/Overlay/useOverlay.tsx +0 -461
- package/src/Portal/component.tsx +0 -54
- package/src/Portal/index.ts +0 -5
- package/src/Text/component.tsx +0 -67
- package/src/Text/index.ts +0 -5
- package/src/Text/styled.ts +0 -30
- package/src/Util/component.tsx +0 -43
- package/src/Util/index.ts +0 -5
- package/src/__tests__/Content.test.tsx +0 -123
- package/src/__tests__/Element-slot-reactivity.browser.test.tsx +0 -152
- package/src/__tests__/Element.test.ts +0 -819
- package/src/__tests__/Iterator.test.ts +0 -492
- package/src/__tests__/Iterator.types.test.ts +0 -237
- package/src/__tests__/List.test.ts +0 -199
- package/src/__tests__/Overlay.test.ts +0 -492
- package/src/__tests__/Portal.test.ts +0 -156
- package/src/__tests__/Text.test.ts +0 -274
- package/src/__tests__/Util.test.ts +0 -63
- package/src/__tests__/Wrapper-innerhtml.test.tsx +0 -178
- package/src/__tests__/Wrapper.test.tsx +0 -196
- package/src/__tests__/elements.browser.test.tsx +0 -132
- package/src/__tests__/equalBeforeAfter.test.ts +0 -122
- package/src/__tests__/helpers.test.ts +0 -65
- package/src/__tests__/integration.test.tsx +0 -118
- package/src/__tests__/internElementBundle.test.ts +0 -102
- package/src/__tests__/iterator-function-children.test.tsx +0 -120
- package/src/__tests__/native-markers.test.ts +0 -13
- package/src/__tests__/overlayContext.test.tsx +0 -78
- package/src/__tests__/perf-stress.browser.test.tsx +0 -119
- package/src/__tests__/positioning.test.ts +0 -90
- package/src/__tests__/responsiveProps.test.ts +0 -328
- package/src/__tests__/slot-component-reference.test.tsx +0 -157
- package/src/__tests__/useOverlay.test.ts +0 -1336
- package/src/__tests__/utils.test.ts +0 -69
- package/src/__tests__/wrapper-block-cascade.test.ts +0 -121
- package/src/constants.ts +0 -1
- package/src/env.d.ts +0 -6
- package/src/helpers/Content/component.tsx +0 -75
- package/src/helpers/Content/index.ts +0 -3
- package/src/helpers/Content/styled.ts +0 -105
- package/src/helpers/Content/types.ts +0 -49
- package/src/helpers/Iterator/component.tsx +0 -316
- package/src/helpers/Iterator/index.ts +0 -30
- package/src/helpers/Iterator/types.ts +0 -138
- package/src/helpers/Wrapper/component.tsx +0 -180
- package/src/helpers/Wrapper/constants.ts +0 -10
- package/src/helpers/Wrapper/index.ts +0 -3
- package/src/helpers/Wrapper/styled.ts +0 -64
- package/src/helpers/Wrapper/types.ts +0 -56
- package/src/helpers/Wrapper/utils.ts +0 -7
- package/src/helpers/index.ts +0 -4
- package/src/helpers/internElementBundle.ts +0 -37
- package/src/helpers/isPyreonComponent.ts +0 -46
- package/src/index.ts +0 -42
- package/src/manifest.ts +0 -190
- package/src/tests/manifest-snapshot.test.ts +0 -45
- package/src/types.ts +0 -112
- 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
|
-
})
|