@pyreon/runtime-dom 0.11.2 → 0.11.4
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/lib/analysis/index.js.html +1 -1
- package/lib/index.js +14 -12
- package/lib/index.js.map +1 -1
- package/lib/types/index.d.ts.map +1 -1
- package/package.json +3 -3
- package/src/delegate.ts +2 -2
- package/src/hydrate.ts +8 -4
- package/src/keep-alive.ts +2 -1
- package/src/mount.ts +6 -5
- package/src/props.ts +7 -5
- package/src/tests/props.test.ts +463 -0
- package/src/tests/show-context.test.ts +177 -0
- package/src/tests/transition.test.ts +550 -0
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
import { createContext, Fragment, h, provide, useContext } from "@pyreon/core"
|
|
2
|
+
import { signal } from "@pyreon/reactivity"
|
|
3
|
+
import { mount } from "@pyreon/runtime-dom"
|
|
4
|
+
import { describe, expect, it } from "vitest"
|
|
5
|
+
|
|
6
|
+
const TestCtx = createContext("default")
|
|
7
|
+
|
|
8
|
+
describe("context inheritance through reactive boundaries", () => {
|
|
9
|
+
it("child inside reactive accessor inherits parent context", async () => {
|
|
10
|
+
let childValue: string | undefined
|
|
11
|
+
|
|
12
|
+
function Child() {
|
|
13
|
+
childValue = useContext(TestCtx)
|
|
14
|
+
return h("span", null, childValue)
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function Parent() {
|
|
18
|
+
provide(TestCtx, "from-parent")
|
|
19
|
+
const show = signal(false)
|
|
20
|
+
setTimeout(() => show.set(true), 10)
|
|
21
|
+
return () => (show() ? h(Child, null) : null)
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const container = document.createElement("div")
|
|
25
|
+
mount(h(Parent, null), container)
|
|
26
|
+
await new Promise((r) => setTimeout(r, 50))
|
|
27
|
+
expect(childValue).toBe("from-parent")
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
it("deeply nested context survives through multiple reactive layers", async () => {
|
|
31
|
+
let innerValue: string | undefined
|
|
32
|
+
|
|
33
|
+
function Inner() {
|
|
34
|
+
innerValue = useContext(TestCtx)
|
|
35
|
+
return h("span", null, innerValue)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function Middle() {
|
|
39
|
+
const show = signal(false)
|
|
40
|
+
setTimeout(() => show.set(true), 10)
|
|
41
|
+
return () => (show() ? h(Inner, null) : null)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function Outer() {
|
|
45
|
+
provide(TestCtx, "outer-value")
|
|
46
|
+
const show = signal(false)
|
|
47
|
+
setTimeout(() => show.set(true), 5)
|
|
48
|
+
return () => (show() ? h(Middle, null) : null)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const container = document.createElement("div")
|
|
52
|
+
mount(h(Outer, null), container)
|
|
53
|
+
await new Promise((r) => setTimeout(r, 100))
|
|
54
|
+
expect(innerValue).toBe("outer-value")
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
it("sibling providers don't leak context to each other", async () => {
|
|
58
|
+
let childAValue: string | undefined
|
|
59
|
+
let childBValue: string | undefined
|
|
60
|
+
|
|
61
|
+
function ChildA() {
|
|
62
|
+
childAValue = useContext(TestCtx)
|
|
63
|
+
return h("span", null, childAValue)
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function ChildB() {
|
|
67
|
+
childBValue = useContext(TestCtx)
|
|
68
|
+
return h("span", null, childBValue)
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function ProviderA() {
|
|
72
|
+
provide(TestCtx, "A")
|
|
73
|
+
return h(ChildA, null)
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function ProviderB() {
|
|
77
|
+
provide(TestCtx, "B")
|
|
78
|
+
return h(ChildB, null)
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function App() {
|
|
82
|
+
return h(Fragment, null, h(ProviderA, null), h(ProviderB, null))
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const container = document.createElement("div")
|
|
86
|
+
mount(h(App, null), container)
|
|
87
|
+
await new Promise((r) => setTimeout(r, 50))
|
|
88
|
+
|
|
89
|
+
expect(childAValue).toBe("A")
|
|
90
|
+
expect(childBValue).toBe("B")
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
it("Show toggle preserves context across hide/show cycle", async () => {
|
|
94
|
+
let childValue: string | undefined
|
|
95
|
+
let mountCount = 0
|
|
96
|
+
|
|
97
|
+
function Child() {
|
|
98
|
+
mountCount++
|
|
99
|
+
childValue = useContext(TestCtx)
|
|
100
|
+
return h("span", null, childValue)
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function Parent() {
|
|
104
|
+
provide(TestCtx, "persistent")
|
|
105
|
+
const show = signal(true)
|
|
106
|
+
|
|
107
|
+
// Hide then show again
|
|
108
|
+
setTimeout(() => show.set(false), 10)
|
|
109
|
+
setTimeout(() => show.set(true), 30)
|
|
110
|
+
|
|
111
|
+
return () => (show() ? h(Child, null) : null)
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const container = document.createElement("div")
|
|
115
|
+
mount(h(Parent, null), container)
|
|
116
|
+
await new Promise((r) => setTimeout(r, 100))
|
|
117
|
+
|
|
118
|
+
expect(childValue).toBe("persistent")
|
|
119
|
+
expect(mountCount).toBe(2) // mounted twice (initial + re-show)
|
|
120
|
+
})
|
|
121
|
+
|
|
122
|
+
it("reactive context getter updates JSX without re-running component", async () => {
|
|
123
|
+
const ModeCtx = createContext<() => string>(() => "light")
|
|
124
|
+
let renderCount = 0
|
|
125
|
+
|
|
126
|
+
function Child() {
|
|
127
|
+
const getMode = useContext(ModeCtx)
|
|
128
|
+
renderCount++
|
|
129
|
+
// Reading the getter inside a reactive accessor — updates when mode changes
|
|
130
|
+
return h("span", null, () => getMode())
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function Parent() {
|
|
134
|
+
const mode = signal<string>("light")
|
|
135
|
+
provide(ModeCtx, () => mode())
|
|
136
|
+
setTimeout(() => mode.set("dark"), 10)
|
|
137
|
+
return h(Child, null)
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const container = document.createElement("div")
|
|
141
|
+
mount(h(Parent, null), container)
|
|
142
|
+
|
|
143
|
+
expect(container.textContent).toBe("light")
|
|
144
|
+
|
|
145
|
+
await new Promise((r) => setTimeout(r, 50))
|
|
146
|
+
expect(container.textContent).toBe("dark")
|
|
147
|
+
// Component setup ran once — JSX expression re-evaluated reactively
|
|
148
|
+
expect(renderCount).toBe(1)
|
|
149
|
+
})
|
|
150
|
+
|
|
151
|
+
it("nested Show inside For with context", async () => {
|
|
152
|
+
const ItemCtx = createContext("none")
|
|
153
|
+
const collected: string[] = []
|
|
154
|
+
|
|
155
|
+
function Item() {
|
|
156
|
+
const val = useContext(ItemCtx)
|
|
157
|
+
collected.push(val)
|
|
158
|
+
return h("li", null, val)
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
function Parent() {
|
|
162
|
+
provide(ItemCtx, "parent-provided")
|
|
163
|
+
const items = signal([1, 2, 3])
|
|
164
|
+
const show = signal(false)
|
|
165
|
+
setTimeout(() => show.set(true), 10)
|
|
166
|
+
|
|
167
|
+
return () => (show() ? h("ul", null, ...items().map((i) => h(Item, { key: i }))) : null)
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
const container = document.createElement("div")
|
|
171
|
+
mount(h(Parent, null), container)
|
|
172
|
+
await new Promise((r) => setTimeout(r, 50))
|
|
173
|
+
|
|
174
|
+
expect(collected.length).toBe(3)
|
|
175
|
+
expect(collected.every((v) => v === "parent-provided")).toBe(true)
|
|
176
|
+
})
|
|
177
|
+
})
|