@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.
- package/CHANGELOG.md +291 -0
- package/README.md +13 -13
- package/esm/component-factory.spec.js +13 -5
- package/esm/component-factory.spec.js.map +1 -1
- package/esm/components/index.d.ts +4 -1
- package/esm/components/index.d.ts.map +1 -1
- package/esm/components/index.js +4 -1
- package/esm/components/index.js.map +1 -1
- package/esm/components/lazy-load.d.ts +2 -4
- package/esm/components/lazy-load.d.ts.map +1 -1
- package/esm/components/lazy-load.js +40 -24
- package/esm/components/lazy-load.js.map +1 -1
- package/esm/components/lazy-load.spec.js +57 -50
- package/esm/components/lazy-load.spec.js.map +1 -1
- package/esm/components/link-to-route.d.ts +2 -0
- package/esm/components/link-to-route.d.ts.map +1 -1
- package/esm/components/link-to-route.js +3 -2
- package/esm/components/link-to-route.js.map +1 -1
- package/esm/components/link-to-route.spec.js +13 -9
- package/esm/components/link-to-route.spec.js.map +1 -1
- package/esm/components/nested-route-link.d.ts +62 -0
- package/esm/components/nested-route-link.d.ts.map +1 -0
- package/esm/components/nested-route-link.js +66 -0
- package/esm/components/nested-route-link.js.map +1 -0
- package/esm/components/nested-route-link.spec.d.ts +2 -0
- package/esm/components/nested-route-link.spec.d.ts.map +1 -0
- package/esm/components/nested-route-link.spec.js +179 -0
- package/esm/components/nested-route-link.spec.js.map +1 -0
- package/esm/components/nested-route-types.d.ts +37 -0
- package/esm/components/nested-route-types.d.ts.map +1 -0
- package/esm/components/nested-route-types.js +2 -0
- package/esm/components/nested-route-types.js.map +1 -0
- package/esm/components/nested-router.d.ts +103 -0
- package/esm/components/nested-router.d.ts.map +1 -0
- package/esm/components/nested-router.js +178 -0
- package/esm/components/nested-router.js.map +1 -0
- package/esm/components/nested-router.spec.d.ts +2 -0
- package/esm/components/nested-router.spec.d.ts.map +1 -0
- package/esm/components/nested-router.spec.js +659 -0
- package/esm/components/nested-router.spec.js.map +1 -0
- package/esm/components/route-link.d.ts +4 -0
- package/esm/components/route-link.d.ts.map +1 -1
- package/esm/components/route-link.js +5 -5
- package/esm/components/route-link.js.map +1 -1
- package/esm/components/route-link.spec.js +16 -12
- package/esm/components/route-link.spec.js.map +1 -1
- package/esm/components/router.d.ts +20 -2
- package/esm/components/router.d.ts.map +1 -1
- package/esm/components/router.js +3 -0
- package/esm/components/router.js.map +1 -1
- package/esm/components/router.spec.js +75 -74
- package/esm/components/router.spec.js.map +1 -1
- package/esm/initialize.d.ts +11 -0
- package/esm/initialize.d.ts.map +1 -1
- package/esm/initialize.js +5 -0
- package/esm/initialize.js.map +1 -1
- package/esm/jsx.d.ts +83 -2
- package/esm/jsx.d.ts.map +1 -1
- package/esm/models/children-list.d.ts +5 -1
- package/esm/models/children-list.d.ts.map +1 -1
- package/esm/models/partial-element.d.ts +12 -2
- package/esm/models/partial-element.d.ts.map +1 -1
- package/esm/models/render-options.d.ts +89 -3
- package/esm/models/render-options.d.ts.map +1 -1
- package/esm/models/selection-state.d.ts +4 -0
- package/esm/models/selection-state.d.ts.map +1 -1
- package/esm/services/location-service.d.ts +11 -0
- package/esm/services/location-service.d.ts.map +1 -1
- package/esm/services/location-service.js +11 -0
- package/esm/services/location-service.js.map +1 -1
- package/esm/services/resource-manager.d.ts +24 -0
- package/esm/services/resource-manager.d.ts.map +1 -1
- package/esm/services/resource-manager.js +30 -0
- package/esm/services/resource-manager.js.map +1 -1
- package/esm/services/resource-manager.spec.js +93 -0
- package/esm/services/resource-manager.spec.js.map +1 -1
- package/esm/services/screen-service.d.ts +81 -4
- package/esm/services/screen-service.d.ts.map +1 -1
- package/esm/services/screen-service.js +75 -4
- package/esm/services/screen-service.js.map +1 -1
- package/esm/services/screen-service.spec.js +91 -7
- package/esm/services/screen-service.spec.js.map +1 -1
- package/esm/shade-component.d.ts +17 -4
- package/esm/shade-component.d.ts.map +1 -1
- package/esm/shade-component.js +67 -5
- package/esm/shade-component.js.map +1 -1
- package/esm/shade-host-props-ref.integration.spec.d.ts +2 -0
- package/esm/shade-host-props-ref.integration.spec.d.ts.map +1 -0
- package/esm/shade-host-props-ref.integration.spec.js +381 -0
- package/esm/shade-host-props-ref.integration.spec.js.map +1 -0
- package/esm/shade-resources.integration.spec.js +208 -39
- package/esm/shade-resources.integration.spec.js.map +1 -1
- package/esm/shade.d.ts +20 -17
- package/esm/shade.d.ts.map +1 -1
- package/esm/shade.js +172 -33
- package/esm/shade.js.map +1 -1
- package/esm/shade.spec.js +31 -30
- package/esm/shade.spec.js.map +1 -1
- package/esm/shades.integration.spec.js +135 -72
- package/esm/shades.integration.spec.js.map +1 -1
- package/esm/style-manager.d.ts +2 -2
- package/esm/style-manager.js +2 -2
- package/esm/svg-types.d.ts +389 -0
- package/esm/svg-types.d.ts.map +1 -0
- package/esm/svg-types.js +9 -0
- package/esm/svg-types.js.map +1 -0
- package/esm/svg.d.ts +15 -0
- package/esm/svg.d.ts.map +1 -0
- package/esm/svg.js +76 -0
- package/esm/svg.js.map +1 -0
- package/esm/svg.spec.d.ts +2 -0
- package/esm/svg.spec.d.ts.map +1 -0
- package/esm/svg.spec.js +80 -0
- package/esm/svg.spec.js.map +1 -0
- package/esm/vnode.d.ts +103 -0
- package/esm/vnode.d.ts.map +1 -0
- package/esm/vnode.integration.spec.d.ts +2 -0
- package/esm/vnode.integration.spec.d.ts.map +1 -0
- package/esm/vnode.integration.spec.js +494 -0
- package/esm/vnode.integration.spec.js.map +1 -0
- package/esm/vnode.js +453 -0
- package/esm/vnode.js.map +1 -0
- package/esm/vnode.spec.d.ts +2 -0
- package/esm/vnode.spec.d.ts.map +1 -0
- package/esm/vnode.spec.js +473 -0
- package/esm/vnode.spec.js.map +1 -0
- package/package.json +3 -3
- package/src/component-factory.spec.tsx +18 -5
- package/src/components/index.ts +4 -1
- package/src/components/lazy-load.spec.tsx +82 -75
- package/src/components/lazy-load.tsx +49 -27
- package/src/components/link-to-route.spec.tsx +25 -21
- package/src/components/link-to-route.tsx +4 -2
- package/src/components/nested-route-link.spec.tsx +303 -0
- package/src/components/nested-route-link.tsx +100 -0
- package/src/components/nested-route-types.ts +42 -0
- package/src/components/nested-router.spec.tsx +817 -0
- package/src/components/nested-router.tsx +256 -0
- package/src/components/route-link.spec.tsx +22 -18
- package/src/components/route-link.tsx +6 -5
- package/src/components/router.spec.tsx +109 -108
- package/src/components/router.tsx +15 -2
- package/src/initialize.ts +12 -0
- package/src/jsx.ts +129 -2
- package/src/models/children-list.ts +7 -1
- package/src/models/partial-element.ts +13 -2
- package/src/models/render-options.ts +90 -3
- package/src/models/selection-state.ts +4 -0
- package/src/services/location-service.tsx +11 -0
- package/src/services/resource-manager.spec.ts +116 -0
- package/src/services/resource-manager.ts +30 -0
- package/src/services/screen-service.spec.ts +109 -7
- package/src/services/screen-service.ts +81 -4
- package/src/shade-component.ts +72 -6
- package/src/shade-host-props-ref.integration.spec.tsx +460 -0
- package/src/shade-resources.integration.spec.tsx +276 -52
- package/src/shade.spec.tsx +40 -39
- package/src/shade.ts +186 -58
- package/src/shades.integration.spec.tsx +154 -80
- package/src/style-manager.ts +2 -2
- package/src/svg-types.ts +437 -0
- package/src/svg.spec.ts +89 -0
- package/src/svg.ts +78 -0
- package/src/vnode.integration.spec.tsx +657 -0
- package/src/vnode.spec.ts +579 -0
- 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
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
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
|
-
|
|
42
|
-
|
|
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
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
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
|
-
|
|
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
|
-
|
|
54
|
-
|
|
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
|
-
|
|
148
|
+
const renderCounter = vi.fn()
|
|
149
|
+
const obs = new ObservableValue(0)
|
|
57
150
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
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
|
-
|
|
65
|
-
|
|
66
|
-
|
|
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
|
-
|
|
70
|
-
|
|
172
|
+
initializeShadeRoot({
|
|
173
|
+
injector,
|
|
174
|
+
rootElement,
|
|
175
|
+
jsxElement: <ExampleComponent />,
|
|
176
|
+
})
|
|
177
|
+
await flushUpdates()
|
|
71
178
|
|
|
72
|
-
|
|
179
|
+
const element = document.querySelector('shades-example-manual-dom-update') as JSX.Element
|
|
73
180
|
|
|
74
|
-
|
|
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
|
-
|
|
77
|
-
|
|
186
|
+
// Change the observable value
|
|
187
|
+
obs.setValue(42)
|
|
78
188
|
|
|
79
|
-
|
|
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
|
})
|
package/src/shade.spec.tsx
CHANGED
|
@@ -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
|
|
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
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
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
|
})
|