@pyreon/runtime-dom 0.24.5 → 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 +5 -9
- package/src/delegate.ts +0 -98
- package/src/devtools.ts +0 -339
- package/src/env.d.ts +0 -6
- package/src/hydrate.ts +0 -450
- package/src/hydration-debug.ts +0 -129
- package/src/index.ts +0 -83
- package/src/keep-alive-entry.ts +0 -3
- package/src/keep-alive.ts +0 -83
- package/src/manifest.ts +0 -236
- package/src/mount.ts +0 -597
- package/src/nodes.ts +0 -896
- package/src/props.ts +0 -474
- package/src/template.ts +0 -523
- package/src/tests/callback-ref-unmount.browser.test.ts +0 -62
- package/src/tests/callback-ref-unmount.test.ts +0 -52
- package/src/tests/compiler-integration.test.tsx +0 -508
- package/src/tests/coverage-gaps.test.ts +0 -3183
- package/src/tests/coverage.test.ts +0 -1140
- package/src/tests/ctx-stack-growth-repro.test.tsx +0 -158
- package/src/tests/dev-gate-pattern.test.ts +0 -46
- package/src/tests/dev-gate-treeshake.test.ts +0 -256
- package/src/tests/error-boundary-stack-leak-repro.test.tsx +0 -133
- package/src/tests/fanout-repro.test.tsx +0 -219
- package/src/tests/hydration-integration.test.tsx +0 -540
- package/src/tests/keyed-array-in-for-batched-toggle.browser.test.ts +0 -140
- package/src/tests/lifecycle-integration.test.tsx +0 -342
- package/src/tests/lis-prepend.browser.test.ts +0 -99
- package/src/tests/manifest-snapshot.test.ts +0 -85
- package/src/tests/mount.test.ts +0 -3529
- package/src/tests/native-markers.test.ts +0 -19
- package/src/tests/props.test.ts +0 -581
- package/src/tests/reactive-props.test.ts +0 -270
- package/src/tests/real-world-integration.test.tsx +0 -714
- package/src/tests/rs-collapse-dyn-h.browser.test.ts +0 -303
- package/src/tests/rs-collapse-dyn.browser.test.ts +0 -316
- package/src/tests/rs-collapse-h.browser.test.ts +0 -152
- package/src/tests/rs-collapse-h.test.ts +0 -237
- package/src/tests/rs-collapse.browser.test.ts +0 -128
- package/src/tests/runtime-dom.browser.test.ts +0 -409
- package/src/tests/setup.ts +0 -3
- package/src/tests/show-context.test.ts +0 -270
- package/src/tests/show-of-for-batched-toggle.browser.test.ts +0 -122
- package/src/tests/ssr-xss-round-trip.browser.test.ts +0 -93
- package/src/tests/style-key-removal.browser.test.ts +0 -54
- package/src/tests/style-key-removal.test.ts +0 -88
- package/src/tests/template.test.ts +0 -383
- package/src/tests/transition-timeout-leak.test.ts +0 -126
- package/src/tests/transition.test.ts +0 -568
- package/src/tests/verified-correct-probes.test.ts +0 -56
- package/src/transition-entry.ts +0 -7
- package/src/transition-group.ts +0 -350
- package/src/transition.ts +0 -245
|
@@ -1,270 +0,0 @@
|
|
|
1
|
-
import { h, splitProps } from '@pyreon/core'
|
|
2
|
-
import { _rp } from '@pyreon/core'
|
|
3
|
-
import { signal } from '@pyreon/reactivity'
|
|
4
|
-
import { mount } from '../index'
|
|
5
|
-
|
|
6
|
-
describe('reactive component props', () => {
|
|
7
|
-
let container: HTMLDivElement
|
|
8
|
-
|
|
9
|
-
beforeEach(() => {
|
|
10
|
-
container = document.createElement('div')
|
|
11
|
-
document.body.appendChild(container)
|
|
12
|
-
})
|
|
13
|
-
|
|
14
|
-
afterEach(() => {
|
|
15
|
-
container.remove()
|
|
16
|
-
})
|
|
17
|
-
|
|
18
|
-
it('updates DOM when a reactive prop changes (no remount)', () => {
|
|
19
|
-
const active = signal(false)
|
|
20
|
-
let mountCount = 0
|
|
21
|
-
|
|
22
|
-
const Inner = (props: any) => {
|
|
23
|
-
mountCount++
|
|
24
|
-
return h('span', { 'data-active': () => String(props.active) })
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
const Outer = () => {
|
|
28
|
-
return h(Inner, { active: _rp(() => active()) })
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
mount(h(Outer, null), container)
|
|
32
|
-
|
|
33
|
-
expect(mountCount).toBe(1)
|
|
34
|
-
expect(container.querySelector('span')?.getAttribute('data-active')).toBe('false')
|
|
35
|
-
|
|
36
|
-
active.set(true)
|
|
37
|
-
|
|
38
|
-
// Inner should NOT have remounted — mountCount stays 1
|
|
39
|
-
expect(mountCount).toBe(1)
|
|
40
|
-
expect(container.querySelector('span')?.getAttribute('data-active')).toBe('true')
|
|
41
|
-
})
|
|
42
|
-
|
|
43
|
-
it('non-reactive props stay static', () => {
|
|
44
|
-
let value = ''
|
|
45
|
-
const Comp = (props: any) => {
|
|
46
|
-
value = props.label
|
|
47
|
-
return h('span', null, props.label)
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
mount(h(Comp, { label: 'hello' }), container)
|
|
51
|
-
expect(value).toBe('hello')
|
|
52
|
-
})
|
|
53
|
-
|
|
54
|
-
it('event handlers are NOT wrapped as getters', () => {
|
|
55
|
-
let clicked = false
|
|
56
|
-
const Comp = (props: any) => {
|
|
57
|
-
return h('button', { onClick: props.onClick }, 'click')
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
mount(h(Comp, { onClick: () => { clicked = true } }), container)
|
|
61
|
-
container.querySelector('button')?.click()
|
|
62
|
-
expect(clicked).toBe(true)
|
|
63
|
-
})
|
|
64
|
-
|
|
65
|
-
it('user accessor props (like Show when) stay as functions', () => {
|
|
66
|
-
let receivedWhen: unknown
|
|
67
|
-
const MyShow = (props: any) => {
|
|
68
|
-
receivedWhen = props.when
|
|
69
|
-
return props.when() ? h('div', null, 'visible') : null
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
const show = signal(true)
|
|
73
|
-
mount(h(MyShow, { when: () => show() }), container)
|
|
74
|
-
|
|
75
|
-
// when should be a function (NOT converted to getter)
|
|
76
|
-
expect(typeof receivedWhen).toBe('function')
|
|
77
|
-
})
|
|
78
|
-
|
|
79
|
-
it('props.x read inside effect tracks the signal', () => {
|
|
80
|
-
const count = signal(0)
|
|
81
|
-
const values: number[] = []
|
|
82
|
-
|
|
83
|
-
const Comp = (props: any) => {
|
|
84
|
-
// Read props.count inside a reactive child accessor
|
|
85
|
-
return h('div', null, () => {
|
|
86
|
-
const v = props.count
|
|
87
|
-
values.push(v)
|
|
88
|
-
return String(v)
|
|
89
|
-
})
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
mount(h(Comp, { count: _rp(() => count()) }), container)
|
|
93
|
-
expect(container.textContent).toBe('0')
|
|
94
|
-
|
|
95
|
-
count.set(5)
|
|
96
|
-
expect(container.textContent).toBe('5')
|
|
97
|
-
|
|
98
|
-
count.set(10)
|
|
99
|
-
expect(container.textContent).toBe('10')
|
|
100
|
-
|
|
101
|
-
// Values tracked — getter called multiple times proves reactivity
|
|
102
|
-
expect(values.length).toBeGreaterThan(1)
|
|
103
|
-
expect(values[values.length - 1]).toBe(10)
|
|
104
|
-
})
|
|
105
|
-
|
|
106
|
-
it('splitProps preserves getter reactivity', () => {
|
|
107
|
-
const active = signal(false)
|
|
108
|
-
|
|
109
|
-
const Comp = (props: any) => {
|
|
110
|
-
const [own, rest] = splitProps(props, ['active'])
|
|
111
|
-
// own.active should be a getter — reactive when read in tracked scope
|
|
112
|
-
return h('div', {
|
|
113
|
-
'data-active': () => String(own.active),
|
|
114
|
-
'data-rest': () => JSON.stringify(Object.keys(rest)),
|
|
115
|
-
})
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
mount(h(Comp, { active: _rp(() => active()), label: 'test' }), container)
|
|
119
|
-
expect(container.querySelector('div')?.getAttribute('data-active')).toBe('false')
|
|
120
|
-
|
|
121
|
-
active.set(true)
|
|
122
|
-
expect(container.querySelector('div')?.getAttribute('data-active')).toBe('true')
|
|
123
|
-
})
|
|
124
|
-
|
|
125
|
-
it('VNode prop as single JSX element — stable, not re-created', () => {
|
|
126
|
-
let iconMountCount = 0
|
|
127
|
-
|
|
128
|
-
const Icon = (props: any) => {
|
|
129
|
-
iconMountCount++
|
|
130
|
-
return h('i', { 'data-name': () => props.name })
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
// Simulate what the compiler produces for:
|
|
134
|
-
// <Wrapper icon={<Icon name={_rp(() => name())} />} />
|
|
135
|
-
// The compiler does NOT wrap the outer JSX — Icon is created once
|
|
136
|
-
// with reactive inner props.
|
|
137
|
-
const name = signal('check')
|
|
138
|
-
const iconVNode = h(Icon, { name: _rp(() => name()) })
|
|
139
|
-
|
|
140
|
-
const Wrapper = (props: any) => {
|
|
141
|
-
return h('div', null, props.icon)
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
mount(h(Wrapper, { icon: iconVNode }), container)
|
|
145
|
-
expect(iconMountCount).toBe(1)
|
|
146
|
-
expect(container.querySelector('i')?.getAttribute('data-name')).toBe('check')
|
|
147
|
-
|
|
148
|
-
name.set('close')
|
|
149
|
-
expect(iconMountCount).toBe(1) // NOT remounted
|
|
150
|
-
expect(container.querySelector('i')?.getAttribute('data-name')).toBe('close')
|
|
151
|
-
})
|
|
152
|
-
|
|
153
|
-
it('multiple reactive props update independently', () => {
|
|
154
|
-
const first = signal('Alice')
|
|
155
|
-
const last = signal('Smith')
|
|
156
|
-
let mountCount = 0
|
|
157
|
-
|
|
158
|
-
const Comp = (props: any) => {
|
|
159
|
-
mountCount++
|
|
160
|
-
return h('div', null,
|
|
161
|
-
h('span', { id: 'first' }, () => props.first),
|
|
162
|
-
h('span', { id: 'last' }, () => props.last),
|
|
163
|
-
)
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
mount(h(Comp, { first: _rp(() => first()), last: _rp(() => last()) }), container)
|
|
167
|
-
expect(mountCount).toBe(1)
|
|
168
|
-
expect(container.querySelector('#first')?.textContent).toBe('Alice')
|
|
169
|
-
expect(container.querySelector('#last')?.textContent).toBe('Smith')
|
|
170
|
-
|
|
171
|
-
first.set('Bob')
|
|
172
|
-
expect(mountCount).toBe(1)
|
|
173
|
-
expect(container.querySelector('#first')?.textContent).toBe('Bob')
|
|
174
|
-
expect(container.querySelector('#last')?.textContent).toBe('Smith')
|
|
175
|
-
|
|
176
|
-
last.set('Jones')
|
|
177
|
-
expect(mountCount).toBe(1)
|
|
178
|
-
expect(container.querySelector('#first')?.textContent).toBe('Bob')
|
|
179
|
-
expect(container.querySelector('#last')?.textContent).toBe('Jones')
|
|
180
|
-
})
|
|
181
|
-
|
|
182
|
-
it('reactive and static props coexist', () => {
|
|
183
|
-
const count = signal(0)
|
|
184
|
-
let mountCount = 0
|
|
185
|
-
|
|
186
|
-
const Comp = (props: any) => {
|
|
187
|
-
mountCount++
|
|
188
|
-
return h('div', null,
|
|
189
|
-
h('span', { id: 'dynamic' }, () => String(props.count)),
|
|
190
|
-
h('span', { id: 'static' }, props.label),
|
|
191
|
-
)
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
mount(h(Comp, { count: _rp(() => count()), label: 'fixed' }), container)
|
|
195
|
-
expect(mountCount).toBe(1)
|
|
196
|
-
expect(container.querySelector('#dynamic')?.textContent).toBe('0')
|
|
197
|
-
expect(container.querySelector('#static')?.textContent).toBe('fixed')
|
|
198
|
-
|
|
199
|
-
count.set(42)
|
|
200
|
-
expect(mountCount).toBe(1)
|
|
201
|
-
expect(container.querySelector('#dynamic')?.textContent).toBe('42')
|
|
202
|
-
expect(container.querySelector('#static')?.textContent).toBe('fixed')
|
|
203
|
-
})
|
|
204
|
-
|
|
205
|
-
it('nested components with reactive props from same signal', () => {
|
|
206
|
-
const value = signal('hello')
|
|
207
|
-
let outerMounts = 0
|
|
208
|
-
let innerMounts = 0
|
|
209
|
-
|
|
210
|
-
const Inner = (props: any) => {
|
|
211
|
-
innerMounts++
|
|
212
|
-
return h('span', { id: 'inner' }, () => props.text)
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
const Outer = (props: any) => {
|
|
216
|
-
outerMounts++
|
|
217
|
-
return h('div', { id: 'outer' },
|
|
218
|
-
() => props.label,
|
|
219
|
-
h(Inner, { text: _rp(() => value()) }),
|
|
220
|
-
)
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
mount(h(Outer, { label: _rp(() => value()) }), container)
|
|
224
|
-
expect(outerMounts).toBe(1)
|
|
225
|
-
expect(innerMounts).toBe(1)
|
|
226
|
-
expect(container.querySelector('#outer')?.textContent).toBe('hellohello')
|
|
227
|
-
|
|
228
|
-
value.set('world')
|
|
229
|
-
expect(outerMounts).toBe(1)
|
|
230
|
-
expect(innerMounts).toBe(1)
|
|
231
|
-
expect(container.querySelector('#outer')?.textContent).toBe('worldworld')
|
|
232
|
-
})
|
|
233
|
-
|
|
234
|
-
it('prop changing to undefined', () => {
|
|
235
|
-
const title = signal<string | undefined>('visible')
|
|
236
|
-
|
|
237
|
-
const Comp = (props: any) => {
|
|
238
|
-
return h('div', null, () => props.title ?? 'empty')
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
mount(h(Comp, { title: _rp(() => title()) }), container)
|
|
242
|
-
expect(container.textContent).toBe('visible')
|
|
243
|
-
|
|
244
|
-
title.set(undefined)
|
|
245
|
-
expect(container.textContent).toBe('empty')
|
|
246
|
-
|
|
247
|
-
title.set('back')
|
|
248
|
-
expect(container.textContent).toBe('back')
|
|
249
|
-
})
|
|
250
|
-
|
|
251
|
-
it('rapid signal updates produce correct final value', () => {
|
|
252
|
-
const count = signal(0)
|
|
253
|
-
let mountCount = 0
|
|
254
|
-
|
|
255
|
-
const Comp = (props: any) => {
|
|
256
|
-
mountCount++
|
|
257
|
-
return h('div', null, () => String(props.count))
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
mount(h(Comp, { count: _rp(() => count()) }), container)
|
|
261
|
-
expect(mountCount).toBe(1)
|
|
262
|
-
|
|
263
|
-
for (let i = 1; i <= 100; i++) {
|
|
264
|
-
count.set(i)
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
expect(mountCount).toBe(1)
|
|
268
|
-
expect(container.textContent).toBe('100')
|
|
269
|
-
})
|
|
270
|
-
})
|