@furystack/shades 11.1.0 → 12.0.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 (166) hide show
  1. package/CHANGELOG.md +291 -0
  2. package/README.md +13 -13
  3. package/esm/component-factory.spec.js +13 -5
  4. package/esm/component-factory.spec.js.map +1 -1
  5. package/esm/components/index.d.ts +4 -1
  6. package/esm/components/index.d.ts.map +1 -1
  7. package/esm/components/index.js +4 -1
  8. package/esm/components/index.js.map +1 -1
  9. package/esm/components/lazy-load.d.ts +2 -4
  10. package/esm/components/lazy-load.d.ts.map +1 -1
  11. package/esm/components/lazy-load.js +40 -24
  12. package/esm/components/lazy-load.js.map +1 -1
  13. package/esm/components/lazy-load.spec.js +57 -50
  14. package/esm/components/lazy-load.spec.js.map +1 -1
  15. package/esm/components/link-to-route.d.ts +2 -0
  16. package/esm/components/link-to-route.d.ts.map +1 -1
  17. package/esm/components/link-to-route.js +3 -2
  18. package/esm/components/link-to-route.js.map +1 -1
  19. package/esm/components/link-to-route.spec.js +13 -9
  20. package/esm/components/link-to-route.spec.js.map +1 -1
  21. package/esm/components/nested-route-link.d.ts +62 -0
  22. package/esm/components/nested-route-link.d.ts.map +1 -0
  23. package/esm/components/nested-route-link.js +66 -0
  24. package/esm/components/nested-route-link.js.map +1 -0
  25. package/esm/components/nested-route-link.spec.d.ts +2 -0
  26. package/esm/components/nested-route-link.spec.d.ts.map +1 -0
  27. package/esm/components/nested-route-link.spec.js +179 -0
  28. package/esm/components/nested-route-link.spec.js.map +1 -0
  29. package/esm/components/nested-route-types.d.ts +37 -0
  30. package/esm/components/nested-route-types.d.ts.map +1 -0
  31. package/esm/components/nested-route-types.js +2 -0
  32. package/esm/components/nested-route-types.js.map +1 -0
  33. package/esm/components/nested-router.d.ts +103 -0
  34. package/esm/components/nested-router.d.ts.map +1 -0
  35. package/esm/components/nested-router.js +178 -0
  36. package/esm/components/nested-router.js.map +1 -0
  37. package/esm/components/nested-router.spec.d.ts +2 -0
  38. package/esm/components/nested-router.spec.d.ts.map +1 -0
  39. package/esm/components/nested-router.spec.js +659 -0
  40. package/esm/components/nested-router.spec.js.map +1 -0
  41. package/esm/components/route-link.d.ts +4 -0
  42. package/esm/components/route-link.d.ts.map +1 -1
  43. package/esm/components/route-link.js +5 -5
  44. package/esm/components/route-link.js.map +1 -1
  45. package/esm/components/route-link.spec.js +16 -12
  46. package/esm/components/route-link.spec.js.map +1 -1
  47. package/esm/components/router.d.ts +20 -2
  48. package/esm/components/router.d.ts.map +1 -1
  49. package/esm/components/router.js +3 -0
  50. package/esm/components/router.js.map +1 -1
  51. package/esm/components/router.spec.js +75 -74
  52. package/esm/components/router.spec.js.map +1 -1
  53. package/esm/initialize.d.ts +11 -0
  54. package/esm/initialize.d.ts.map +1 -1
  55. package/esm/initialize.js +5 -0
  56. package/esm/initialize.js.map +1 -1
  57. package/esm/jsx.d.ts +83 -2
  58. package/esm/jsx.d.ts.map +1 -1
  59. package/esm/models/children-list.d.ts +5 -1
  60. package/esm/models/children-list.d.ts.map +1 -1
  61. package/esm/models/partial-element.d.ts +12 -2
  62. package/esm/models/partial-element.d.ts.map +1 -1
  63. package/esm/models/render-options.d.ts +89 -3
  64. package/esm/models/render-options.d.ts.map +1 -1
  65. package/esm/models/selection-state.d.ts +4 -0
  66. package/esm/models/selection-state.d.ts.map +1 -1
  67. package/esm/services/location-service.d.ts +11 -0
  68. package/esm/services/location-service.d.ts.map +1 -1
  69. package/esm/services/location-service.js +11 -0
  70. package/esm/services/location-service.js.map +1 -1
  71. package/esm/services/resource-manager.d.ts +24 -0
  72. package/esm/services/resource-manager.d.ts.map +1 -1
  73. package/esm/services/resource-manager.js +30 -0
  74. package/esm/services/resource-manager.js.map +1 -1
  75. package/esm/services/resource-manager.spec.js +93 -0
  76. package/esm/services/resource-manager.spec.js.map +1 -1
  77. package/esm/services/screen-service.d.ts +81 -4
  78. package/esm/services/screen-service.d.ts.map +1 -1
  79. package/esm/services/screen-service.js +75 -4
  80. package/esm/services/screen-service.js.map +1 -1
  81. package/esm/services/screen-service.spec.js +91 -7
  82. package/esm/services/screen-service.spec.js.map +1 -1
  83. package/esm/shade-component.d.ts +17 -4
  84. package/esm/shade-component.d.ts.map +1 -1
  85. package/esm/shade-component.js +67 -5
  86. package/esm/shade-component.js.map +1 -1
  87. package/esm/shade-host-props-ref.integration.spec.d.ts +2 -0
  88. package/esm/shade-host-props-ref.integration.spec.d.ts.map +1 -0
  89. package/esm/shade-host-props-ref.integration.spec.js +381 -0
  90. package/esm/shade-host-props-ref.integration.spec.js.map +1 -0
  91. package/esm/shade-resources.integration.spec.js +208 -39
  92. package/esm/shade-resources.integration.spec.js.map +1 -1
  93. package/esm/shade.d.ts +20 -17
  94. package/esm/shade.d.ts.map +1 -1
  95. package/esm/shade.js +172 -33
  96. package/esm/shade.js.map +1 -1
  97. package/esm/shade.spec.js +31 -30
  98. package/esm/shade.spec.js.map +1 -1
  99. package/esm/shades.integration.spec.js +135 -72
  100. package/esm/shades.integration.spec.js.map +1 -1
  101. package/esm/style-manager.d.ts +2 -2
  102. package/esm/style-manager.js +2 -2
  103. package/esm/svg-types.d.ts +389 -0
  104. package/esm/svg-types.d.ts.map +1 -0
  105. package/esm/svg-types.js +9 -0
  106. package/esm/svg-types.js.map +1 -0
  107. package/esm/svg.d.ts +15 -0
  108. package/esm/svg.d.ts.map +1 -0
  109. package/esm/svg.js +76 -0
  110. package/esm/svg.js.map +1 -0
  111. package/esm/svg.spec.d.ts +2 -0
  112. package/esm/svg.spec.d.ts.map +1 -0
  113. package/esm/svg.spec.js +80 -0
  114. package/esm/svg.spec.js.map +1 -0
  115. package/esm/vnode.d.ts +103 -0
  116. package/esm/vnode.d.ts.map +1 -0
  117. package/esm/vnode.integration.spec.d.ts +2 -0
  118. package/esm/vnode.integration.spec.d.ts.map +1 -0
  119. package/esm/vnode.integration.spec.js +494 -0
  120. package/esm/vnode.integration.spec.js.map +1 -0
  121. package/esm/vnode.js +453 -0
  122. package/esm/vnode.js.map +1 -0
  123. package/esm/vnode.spec.d.ts +2 -0
  124. package/esm/vnode.spec.d.ts.map +1 -0
  125. package/esm/vnode.spec.js +473 -0
  126. package/esm/vnode.spec.js.map +1 -0
  127. package/package.json +3 -3
  128. package/src/component-factory.spec.tsx +18 -5
  129. package/src/components/index.ts +4 -1
  130. package/src/components/lazy-load.spec.tsx +82 -75
  131. package/src/components/lazy-load.tsx +49 -27
  132. package/src/components/link-to-route.spec.tsx +25 -21
  133. package/src/components/link-to-route.tsx +4 -2
  134. package/src/components/nested-route-link.spec.tsx +303 -0
  135. package/src/components/nested-route-link.tsx +100 -0
  136. package/src/components/nested-route-types.ts +42 -0
  137. package/src/components/nested-router.spec.tsx +817 -0
  138. package/src/components/nested-router.tsx +256 -0
  139. package/src/components/route-link.spec.tsx +22 -18
  140. package/src/components/route-link.tsx +6 -5
  141. package/src/components/router.spec.tsx +109 -108
  142. package/src/components/router.tsx +15 -2
  143. package/src/initialize.ts +12 -0
  144. package/src/jsx.ts +129 -2
  145. package/src/models/children-list.ts +7 -1
  146. package/src/models/partial-element.ts +13 -2
  147. package/src/models/render-options.ts +90 -3
  148. package/src/models/selection-state.ts +4 -0
  149. package/src/services/location-service.tsx +11 -0
  150. package/src/services/resource-manager.spec.ts +116 -0
  151. package/src/services/resource-manager.ts +30 -0
  152. package/src/services/screen-service.spec.ts +109 -7
  153. package/src/services/screen-service.ts +81 -4
  154. package/src/shade-component.ts +72 -6
  155. package/src/shade-host-props-ref.integration.spec.tsx +460 -0
  156. package/src/shade-resources.integration.spec.tsx +276 -52
  157. package/src/shade.spec.tsx +40 -39
  158. package/src/shade.ts +186 -58
  159. package/src/shades.integration.spec.tsx +154 -80
  160. package/src/style-manager.ts +2 -2
  161. package/src/svg-types.ts +437 -0
  162. package/src/svg.spec.ts +89 -0
  163. package/src/svg.ts +78 -0
  164. package/src/vnode.integration.spec.tsx +657 -0
  165. package/src/vnode.spec.ts +579 -0
  166. package/src/vnode.ts +508 -0
@@ -1,9 +1,9 @@
1
1
  import { Injector } from '@furystack/inject'
2
- import { ObservableValue, sleepAsync } from '@furystack/utils'
2
+ import { ObservableValue, sleepAsync, usingAsync } from '@furystack/utils'
3
3
  import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
4
4
  import { initializeShadeRoot } from './initialize.js'
5
5
  import { createComponent } from './shade-component.js'
6
- import { Shade } from './shade.js'
6
+ import { flushUpdates, Shade } from './shade.js'
7
7
 
8
8
  describe('Shade Resources integration tests', () => {
9
9
  beforeEach(() => {
@@ -14,68 +14,292 @@ describe('Shade Resources integration tests', () => {
14
14
  })
15
15
 
16
16
  it('Should update the component based on a custom observable value change', async () => {
17
- const injector = new Injector()
18
- const rootElement = document.getElementById('root') as HTMLDivElement
19
-
20
- const renderCounter = vi.fn()
21
-
22
- const obs1 = new ObservableValue(0)
23
- const obs2 = new ObservableValue('a')
24
-
25
- const ExampleComponent = Shade({
26
- render: ({ useObservable }) => {
27
- const [value1] = useObservable('obs1', obs1)
28
- const [value2] = useObservable('obs2', obs2)
29
-
30
- renderCounter()
31
- return (
32
- <div>
33
- <div id="val1">{value1}</div>
34
- <div id="val2">{value2}</div>
35
- </div>
36
- )
37
- },
38
- shadowDomName: 'shades-example-resource',
17
+ await usingAsync(new Injector(), async (injector) => {
18
+ const rootElement = document.getElementById('root') as HTMLDivElement
19
+
20
+ const renderCounter = vi.fn()
21
+
22
+ const obs1 = new ObservableValue(0)
23
+ const obs2 = new ObservableValue('a')
24
+
25
+ const ExampleComponent = Shade({
26
+ render: ({ useObservable }) => {
27
+ const [value1] = useObservable('obs1', obs1)
28
+ const [value2] = useObservable('obs2', obs2)
29
+
30
+ renderCounter()
31
+ return (
32
+ <div>
33
+ <div id="val1">{value1}</div>
34
+ <div id="val2">{value2}</div>
35
+ </div>
36
+ )
37
+ },
38
+ shadowDomName: 'shades-example-resource',
39
+ })
40
+
41
+ expect(obs1.getObservers().length).toBe(0)
42
+ expect(obs2.getObservers().length).toBe(0)
43
+
44
+ initializeShadeRoot({
45
+ injector,
46
+ rootElement,
47
+ jsxElement: <ExampleComponent />,
48
+ })
49
+ await flushUpdates()
50
+ expect(document.body.innerHTML).toBe(
51
+ '<div id="root"><shades-example-resource><div><div id="val1">0</div><div id="val2">a</div></div></shades-example-resource></div>',
52
+ )
53
+
54
+ expect(obs1.getObservers().length).toBe(1)
55
+ expect(obs2.getObservers().length).toBe(1)
56
+
57
+ expect(renderCounter).toBeCalledTimes(1)
58
+
59
+ obs1.setValue(1)
60
+ await flushUpdates()
61
+ expect(document.body.innerHTML).toBe(
62
+ '<div id="root"><shades-example-resource><div><div id="val1">1</div><div id="val2">a</div></div></shades-example-resource></div>',
63
+ )
64
+ expect(renderCounter).toBeCalledTimes(2)
65
+
66
+ obs2.setValue('b')
67
+ await flushUpdates()
68
+ expect(document.body.innerHTML).toBe(
69
+ '<div id="root"><shades-example-resource><div><div id="val1">1</div><div id="val2">b</div></div></shades-example-resource></div>',
70
+ )
71
+
72
+ const element = document.querySelector('shades-example-resource') as JSX.Element
73
+ expect(element.getRenderCount()).toBe(3)
74
+
75
+ document.body.innerHTML = ''
76
+
77
+ await sleepAsync(10) // Cleanup can be async
78
+
79
+ expect(obs1.getObservers().length).toBe(0)
80
+ expect(obs2.getObservers().length).toBe(0)
81
+
82
+ expect(renderCounter).toBeCalledTimes(3)
39
83
  })
84
+ })
85
+
86
+ it('Should NOT re-render the component when a custom onChange callback is provided', async () => {
87
+ await usingAsync(new Injector(), async (injector) => {
88
+ const rootElement = document.getElementById('root') as HTMLDivElement
89
+
90
+ const renderCounter = vi.fn()
91
+ const customOnChange = vi.fn()
92
+
93
+ const obs = new ObservableValue(0)
94
+
95
+ const ExampleComponent = Shade({
96
+ render: ({ useObservable }) => {
97
+ const [value] = useObservable('obs', obs, { onChange: customOnChange })
98
+
99
+ renderCounter()
100
+ return <div id="val">{value}</div>
101
+ },
102
+ shadowDomName: 'shades-example-custom-onchange',
103
+ })
104
+
105
+ initializeShadeRoot({
106
+ injector,
107
+ rootElement,
108
+ jsxElement: <ExampleComponent />,
109
+ })
110
+ await flushUpdates()
111
+
112
+ const element = document.querySelector('shades-example-custom-onchange') as JSX.Element
40
113
 
41
- expect(obs1.getObservers().length).toBe(0)
42
- expect(obs2.getObservers().length).toBe(0)
114
+ // Initial render
115
+ expect(element.getRenderCount()).toBe(1)
116
+ expect(renderCounter).toBeCalledTimes(1)
117
+ expect(customOnChange).toBeCalledTimes(0) // Not called until value changes
118
+ expect(document.getElementById('val')?.textContent).toBe('0')
43
119
 
44
- initializeShadeRoot({
45
- injector,
46
- rootElement,
47
- jsxElement: <ExampleComponent />,
120
+ // Change the observable value
121
+ obs.setValue(1)
122
+
123
+ // Custom onChange should be called
124
+ expect(customOnChange).toBeCalledTimes(1)
125
+ expect(customOnChange).toHaveBeenLastCalledWith(1)
126
+
127
+ // But component should NOT re-render
128
+ expect(element.getRenderCount()).toBe(1)
129
+ expect(renderCounter).toBeCalledTimes(1)
130
+
131
+ // DOM should still show old value since no re-render occurred
132
+ expect(document.getElementById('val')?.textContent).toBe('0')
133
+
134
+ // Change again to verify consistent behavior
135
+ obs.setValue(2)
136
+
137
+ expect(customOnChange).toBeCalledTimes(2)
138
+ expect(customOnChange).toHaveBeenLastCalledWith(2)
139
+ expect(element.getRenderCount()).toBe(1)
140
+ expect(renderCounter).toBeCalledTimes(1)
48
141
  })
49
- expect(document.body.innerHTML).toBe(
50
- '<div id="root"><shades-example-resource><div><div id="val1">0</div><div id="val2">a</div></div></shades-example-resource></div>',
51
- )
142
+ })
52
143
 
53
- expect(obs1.getObservers().length).toBe(1)
54
- expect(obs2.getObservers().length).toBe(1)
144
+ it('Should allow manual DOM updates in custom onChange callback without re-render', async () => {
145
+ await usingAsync(new Injector(), async (injector) => {
146
+ const rootElement = document.getElementById('root') as HTMLDivElement
55
147
 
56
- expect(renderCounter).toBeCalledTimes(1)
148
+ const renderCounter = vi.fn()
149
+ const obs = new ObservableValue(0)
57
150
 
58
- obs1.setValue(1)
59
- expect(document.body.innerHTML).toBe(
60
- '<div id="root"><shades-example-resource><div><div id="val1">1</div><div id="val2">a</div></div></shades-example-resource></div>',
61
- )
62
- expect(renderCounter).toBeCalledTimes(2)
151
+ const ExampleComponent = Shade({
152
+ render: ({ useObservable, useRef }) => {
153
+ const valRef = useRef<HTMLDivElement>('manualVal')
154
+ useObservable('obs', obs, {
155
+ onChange: (newValue) => {
156
+ if (valRef.current) {
157
+ valRef.current.textContent = String(newValue)
158
+ }
159
+ },
160
+ })
63
161
 
64
- obs2.setValue('b')
65
- expect(document.body.innerHTML).toBe(
66
- '<div id="root"><shades-example-resource><div><div id="val1">1</div><div id="val2">b</div></div></shades-example-resource></div>',
67
- )
162
+ renderCounter()
163
+ return (
164
+ <div ref={valRef} id="manual-val">
165
+ {obs.getValue()}
166
+ </div>
167
+ )
168
+ },
169
+ shadowDomName: 'shades-example-manual-dom-update',
170
+ })
68
171
 
69
- const element = document.querySelector('shades-example-resource') as JSX.Element
70
- expect(element.getRenderCount()).toBe(3)
172
+ initializeShadeRoot({
173
+ injector,
174
+ rootElement,
175
+ jsxElement: <ExampleComponent />,
176
+ })
177
+ await flushUpdates()
71
178
 
72
- document.body.innerHTML = ''
179
+ const element = document.querySelector('shades-example-manual-dom-update') as JSX.Element
73
180
 
74
- await sleepAsync(10) // Cleanup can be async
181
+ // Initial render
182
+ expect(element.getRenderCount()).toBe(1)
183
+ expect(renderCounter).toBeCalledTimes(1)
184
+ expect(document.getElementById('manual-val')?.textContent).toBe('0')
75
185
 
76
- expect(obs1.getObservers().length).toBe(0)
77
- expect(obs2.getObservers().length).toBe(0)
186
+ // Change the observable value
187
+ obs.setValue(42)
78
188
 
79
- expect(renderCounter).toBeCalledTimes(3)
189
+ // Component should NOT re-render
190
+ expect(element.getRenderCount()).toBe(1)
191
+ expect(renderCounter).toBeCalledTimes(1)
192
+
193
+ // But DOM should be updated via the manual onChange callback
194
+ expect(document.getElementById('manual-val')?.textContent).toBe('42')
195
+
196
+ // Change again
197
+ obs.setValue(100)
198
+
199
+ expect(element.getRenderCount()).toBe(1)
200
+ expect(renderCounter).toBeCalledTimes(1)
201
+ expect(document.getElementById('manual-val')?.textContent).toBe('100')
202
+ })
203
+ })
204
+
205
+ it('Should batch multiple synchronous observable changes into a single render', async () => {
206
+ await usingAsync(new Injector(), async (injector) => {
207
+ const rootElement = document.getElementById('root') as HTMLDivElement
208
+
209
+ const renderCounter = vi.fn()
210
+
211
+ const obs1 = new ObservableValue(0)
212
+ const obs2 = new ObservableValue('a')
213
+ const obs3 = new ObservableValue(false)
214
+
215
+ const ExampleComponent = Shade({
216
+ render: ({ useObservable }) => {
217
+ const [value1] = useObservable('obs1', obs1)
218
+ const [value2] = useObservable('obs2', obs2)
219
+ const [value3] = useObservable('obs3', obs3)
220
+
221
+ renderCounter()
222
+ return (
223
+ <div>
224
+ <span id="v1">{value1}</span>
225
+ <span id="v2">{value2}</span>
226
+ <span id="v3">{String(value3)}</span>
227
+ </div>
228
+ )
229
+ },
230
+ shadowDomName: 'shades-example-batching',
231
+ })
232
+
233
+ initializeShadeRoot({
234
+ injector,
235
+ rootElement,
236
+ jsxElement: <ExampleComponent />,
237
+ })
238
+ await flushUpdates()
239
+
240
+ const element = document.querySelector('shades-example-batching') as JSX.Element
241
+
242
+ expect(element.getRenderCount()).toBe(1)
243
+ expect(renderCounter).toBeCalledTimes(1)
244
+
245
+ // Change all three observables synchronously without awaiting in between
246
+ obs1.setValue(42)
247
+ obs2.setValue('z')
248
+ obs3.setValue(true)
249
+
250
+ // Before flushing, the DOM should still reflect the old values
251
+ expect(element.getRenderCount()).toBe(1)
252
+
253
+ await flushUpdates()
254
+
255
+ // After flushing, all changes should be reflected in a single render
256
+ expect(element.getRenderCount()).toBe(2)
257
+ expect(renderCounter).toBeCalledTimes(2)
258
+ expect(document.getElementById('v1')?.textContent).toBe('42')
259
+ expect(document.getElementById('v2')?.textContent).toBe('z')
260
+ expect(document.getElementById('v3')?.textContent).toBe('true')
261
+ })
262
+ })
263
+
264
+ it('Should batch multiple updateComponent() calls into a single render', async () => {
265
+ await usingAsync(new Injector(), async (injector) => {
266
+ const rootElement = document.getElementById('root') as HTMLDivElement
267
+
268
+ const renderCounter = vi.fn()
269
+
270
+ const ExampleComponent = Shade({
271
+ render: () => {
272
+ renderCounter()
273
+ return <div>content</div>
274
+ },
275
+ shadowDomName: 'shades-example-update-batching',
276
+ })
277
+
278
+ initializeShadeRoot({
279
+ injector,
280
+ rootElement,
281
+ jsxElement: <ExampleComponent />,
282
+ })
283
+ await flushUpdates()
284
+
285
+ const element = document.querySelector('shades-example-update-batching') as JSX.Element
286
+
287
+ expect(element.getRenderCount()).toBe(1)
288
+ expect(renderCounter).toBeCalledTimes(1)
289
+
290
+ // Call updateComponent multiple times synchronously
291
+ element.updateComponent()
292
+ element.updateComponent()
293
+ element.updateComponent()
294
+
295
+ // Before flushing, render count should still be 1
296
+ expect(element.getRenderCount()).toBe(1)
297
+
298
+ await flushUpdates()
299
+
300
+ // After flushing, only a single additional render should have occurred
301
+ expect(element.getRenderCount()).toBe(2)
302
+ expect(renderCounter).toBeCalledTimes(2)
303
+ })
80
304
  })
81
305
  })
@@ -27,7 +27,7 @@ describe('Shade edge cases', () => {
27
27
  shadowDomName: 'shade-duplicate-test',
28
28
  render: () => <div>Second</div>,
29
29
  })
30
- }).toThrow("A custom shade with shadow DOM name 'shade-duplicate-test' has already been registered!")
30
+ }).toThrow("A custom shade with name 'shade-duplicate-test' has already been registered!")
31
31
  })
32
32
 
33
33
  it('should include the duplicate name in the error message', () => {
@@ -54,45 +54,46 @@ describe('Shade edge cases', () => {
54
54
  describe('injector from props', () => {
55
55
  it('should use props injector for child component instead of inheriting from parent', async () => {
56
56
  await usingAsync(new Injector(), async (rootInjector) => {
57
- const propsInjector = new Injector()
58
- const rootElement = document.getElementById('root') as HTMLDivElement
59
-
60
- let parentCapturedInjector: Injector | undefined
61
- let childCapturedInjector: Injector | undefined
62
-
63
- const ChildComponent = Shade<{ injector?: Injector }>({
64
- shadowDomName: 'shade-injector-child-props-test',
65
- render: ({ injector }) => {
66
- childCapturedInjector = injector
67
- return <div>Child</div>
68
- },
69
- })
70
-
71
- const ParentComponent = Shade({
72
- shadowDomName: 'shade-injector-parent-props-test',
73
- render: ({ injector, children }) => {
74
- parentCapturedInjector = injector
75
- return <div>{children}</div>
76
- },
57
+ await usingAsync(new Injector(), async (propsInjector) => {
58
+ const rootElement = document.getElementById('root') as HTMLDivElement
59
+
60
+ let parentCapturedInjector: Injector | undefined
61
+ let childCapturedInjector: Injector | undefined
62
+
63
+ const ChildComponent = Shade<{ injector?: Injector }>({
64
+ shadowDomName: 'shade-injector-child-props-test',
65
+ render: ({ injector }) => {
66
+ childCapturedInjector = injector
67
+ return <div>Child</div>
68
+ },
69
+ })
70
+
71
+ const ParentComponent = Shade({
72
+ shadowDomName: 'shade-injector-parent-props-test',
73
+ render: ({ injector, children }) => {
74
+ parentCapturedInjector = injector
75
+ return <div>{children}</div>
76
+ },
77
+ })
78
+
79
+ initializeShadeRoot({
80
+ injector: rootInjector,
81
+ rootElement,
82
+ jsxElement: (
83
+ <ParentComponent>
84
+ <ChildComponent injector={propsInjector} />
85
+ </ParentComponent>
86
+ ),
87
+ })
88
+
89
+ await sleepAsync(10)
90
+
91
+ // Parent should use root injector (inherited from parent)
92
+ expect(parentCapturedInjector).toBe(rootInjector)
93
+ // Child should use the props injector, not the parent's
94
+ expect(childCapturedInjector).toBe(propsInjector)
95
+ expect(childCapturedInjector).not.toBe(rootInjector)
77
96
  })
78
-
79
- initializeShadeRoot({
80
- injector: rootInjector,
81
- rootElement,
82
- jsxElement: (
83
- <ParentComponent>
84
- <ChildComponent injector={propsInjector} />
85
- </ParentComponent>
86
- ),
87
- })
88
-
89
- await sleepAsync(10)
90
-
91
- // Parent should use root injector (inherited from parent)
92
- expect(parentCapturedInjector).toBe(rootInjector)
93
- // Child should use the props injector, not the parent's
94
- expect(childCapturedInjector).toBe(propsInjector)
95
- expect(childCapturedInjector).not.toBe(rootInjector)
96
97
  })
97
98
  })
98
99
  })