@furystack/shades 11.1.0 → 12.0.1
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 +312 -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 +183 -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 +737 -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 +12 -7
- package/esm/components/router.js.map +1 -1
- package/esm/components/router.spec.js +141 -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 +36 -1
- package/esm/services/resource-manager.js.map +1 -1
- package/esm/services/resource-manager.spec.js +102 -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 +8 -9
- 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 +918 -0
- package/src/components/nested-router.tsx +260 -0
- package/src/components/route-link.spec.tsx +22 -18
- package/src/components/route-link.tsx +6 -5
- package/src/components/router.spec.tsx +196 -108
- package/src/components/router.tsx +21 -8
- 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 +128 -0
- package/src/services/resource-manager.ts +36 -1
- 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
|
@@ -0,0 +1,460 @@
|
|
|
1
|
+
import { Injector } from '@furystack/inject'
|
|
2
|
+
import { ObservableValue, usingAsync } from '@furystack/utils'
|
|
3
|
+
import { afterEach, beforeEach, describe, expect, it } from 'vitest'
|
|
4
|
+
import { initializeShadeRoot } from './initialize.js'
|
|
5
|
+
import { createComponent } from './shade-component.js'
|
|
6
|
+
import { flushUpdates, Shade } from './shade.js'
|
|
7
|
+
|
|
8
|
+
describe('useHostProps integration tests', () => {
|
|
9
|
+
beforeEach(() => {
|
|
10
|
+
document.body.innerHTML = '<div id="root"></div>'
|
|
11
|
+
})
|
|
12
|
+
afterEach(() => {
|
|
13
|
+
document.body.innerHTML = ''
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
it('should set data attributes on the host element', async () => {
|
|
17
|
+
await usingAsync(new Injector(), async (injector) => {
|
|
18
|
+
const rootElement = document.getElementById('root') as HTMLDivElement
|
|
19
|
+
|
|
20
|
+
const ExampleComponent = Shade<{ variant: string }>({
|
|
21
|
+
shadowDomName: 'host-props-data-attr-test',
|
|
22
|
+
render: ({ props, useHostProps }) => {
|
|
23
|
+
useHostProps({
|
|
24
|
+
'data-variant': props.variant,
|
|
25
|
+
})
|
|
26
|
+
return <div>content</div>
|
|
27
|
+
},
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
initializeShadeRoot({
|
|
31
|
+
injector,
|
|
32
|
+
rootElement,
|
|
33
|
+
jsxElement: <ExampleComponent variant="primary" />,
|
|
34
|
+
})
|
|
35
|
+
await flushUpdates()
|
|
36
|
+
|
|
37
|
+
const el = document.querySelector('host-props-data-attr-test') as HTMLElement
|
|
38
|
+
expect(el).toBeTruthy()
|
|
39
|
+
expect(el.getAttribute('data-variant')).toBe('primary')
|
|
40
|
+
})
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
it('should set aria attributes on the host element', async () => {
|
|
44
|
+
await usingAsync(new Injector(), async (injector) => {
|
|
45
|
+
const rootElement = document.getElementById('root') as HTMLDivElement
|
|
46
|
+
|
|
47
|
+
const ExampleComponent = Shade({
|
|
48
|
+
shadowDomName: 'host-props-aria-test',
|
|
49
|
+
render: ({ useHostProps }) => {
|
|
50
|
+
useHostProps({
|
|
51
|
+
role: 'progressbar',
|
|
52
|
+
'aria-valuenow': '50',
|
|
53
|
+
'aria-valuemin': '0',
|
|
54
|
+
'aria-valuemax': '100',
|
|
55
|
+
})
|
|
56
|
+
return <div>progress</div>
|
|
57
|
+
},
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
initializeShadeRoot({
|
|
61
|
+
injector,
|
|
62
|
+
rootElement,
|
|
63
|
+
jsxElement: <ExampleComponent />,
|
|
64
|
+
})
|
|
65
|
+
await flushUpdates()
|
|
66
|
+
|
|
67
|
+
const el = document.querySelector('host-props-aria-test') as HTMLElement
|
|
68
|
+
expect(el.getAttribute('role')).toBe('progressbar')
|
|
69
|
+
expect(el.getAttribute('aria-valuenow')).toBe('50')
|
|
70
|
+
expect(el.getAttribute('aria-valuemin')).toBe('0')
|
|
71
|
+
expect(el.getAttribute('aria-valuemax')).toBe('100')
|
|
72
|
+
})
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
it('should apply CSS custom properties via style', async () => {
|
|
76
|
+
await usingAsync(new Injector(), async (injector) => {
|
|
77
|
+
const rootElement = document.getElementById('root') as HTMLDivElement
|
|
78
|
+
|
|
79
|
+
const ExampleComponent = Shade({
|
|
80
|
+
shadowDomName: 'host-props-css-vars-test',
|
|
81
|
+
render: ({ useHostProps }) => {
|
|
82
|
+
useHostProps({
|
|
83
|
+
style: {
|
|
84
|
+
'--my-color': 'red',
|
|
85
|
+
'--my-size': '16px',
|
|
86
|
+
},
|
|
87
|
+
})
|
|
88
|
+
return <div>styled</div>
|
|
89
|
+
},
|
|
90
|
+
})
|
|
91
|
+
|
|
92
|
+
initializeShadeRoot({
|
|
93
|
+
injector,
|
|
94
|
+
rootElement,
|
|
95
|
+
jsxElement: <ExampleComponent />,
|
|
96
|
+
})
|
|
97
|
+
await flushUpdates()
|
|
98
|
+
|
|
99
|
+
const el = document.querySelector('host-props-css-vars-test') as HTMLElement
|
|
100
|
+
expect(el.style.getPropertyValue('--my-color')).toBe('red')
|
|
101
|
+
expect(el.style.getPropertyValue('--my-size')).toBe('16px')
|
|
102
|
+
})
|
|
103
|
+
})
|
|
104
|
+
|
|
105
|
+
it('should apply standard inline styles', async () => {
|
|
106
|
+
await usingAsync(new Injector(), async (injector) => {
|
|
107
|
+
const rootElement = document.getElementById('root') as HTMLDivElement
|
|
108
|
+
|
|
109
|
+
const ExampleComponent = Shade({
|
|
110
|
+
shadowDomName: 'host-props-inline-style-test',
|
|
111
|
+
render: ({ useHostProps }) => {
|
|
112
|
+
useHostProps({
|
|
113
|
+
style: {
|
|
114
|
+
display: 'flex',
|
|
115
|
+
gap: '8px',
|
|
116
|
+
},
|
|
117
|
+
})
|
|
118
|
+
return <div>styled</div>
|
|
119
|
+
},
|
|
120
|
+
})
|
|
121
|
+
|
|
122
|
+
initializeShadeRoot({
|
|
123
|
+
injector,
|
|
124
|
+
rootElement,
|
|
125
|
+
jsxElement: <ExampleComponent />,
|
|
126
|
+
})
|
|
127
|
+
await flushUpdates()
|
|
128
|
+
|
|
129
|
+
const el = document.querySelector('host-props-inline-style-test') as HTMLElement
|
|
130
|
+
expect(el.style.display).toBe('flex')
|
|
131
|
+
expect(el.style.gap).toBe('8px')
|
|
132
|
+
})
|
|
133
|
+
})
|
|
134
|
+
|
|
135
|
+
it('should merge multiple useHostProps calls', async () => {
|
|
136
|
+
await usingAsync(new Injector(), async (injector) => {
|
|
137
|
+
const rootElement = document.getElementById('root') as HTMLDivElement
|
|
138
|
+
|
|
139
|
+
const ExampleComponent = Shade({
|
|
140
|
+
shadowDomName: 'host-props-merge-test',
|
|
141
|
+
render: ({ useHostProps }) => {
|
|
142
|
+
useHostProps({
|
|
143
|
+
'data-first': 'one',
|
|
144
|
+
style: { '--color-a': 'red' },
|
|
145
|
+
})
|
|
146
|
+
useHostProps({
|
|
147
|
+
'data-second': 'two',
|
|
148
|
+
style: { '--color-b': 'blue' },
|
|
149
|
+
})
|
|
150
|
+
return <div>merged</div>
|
|
151
|
+
},
|
|
152
|
+
})
|
|
153
|
+
|
|
154
|
+
initializeShadeRoot({
|
|
155
|
+
injector,
|
|
156
|
+
rootElement,
|
|
157
|
+
jsxElement: <ExampleComponent />,
|
|
158
|
+
})
|
|
159
|
+
await flushUpdates()
|
|
160
|
+
|
|
161
|
+
const el = document.querySelector('host-props-merge-test') as HTMLElement
|
|
162
|
+
expect(el.getAttribute('data-first')).toBe('one')
|
|
163
|
+
expect(el.getAttribute('data-second')).toBe('two')
|
|
164
|
+
expect(el.style.getPropertyValue('--color-a')).toBe('red')
|
|
165
|
+
expect(el.style.getPropertyValue('--color-b')).toBe('blue')
|
|
166
|
+
})
|
|
167
|
+
})
|
|
168
|
+
|
|
169
|
+
it('should remove attributes when they are no longer set on re-render', async () => {
|
|
170
|
+
await usingAsync(new Injector(), async (injector) => {
|
|
171
|
+
const rootElement = document.getElementById('root') as HTMLDivElement
|
|
172
|
+
const showExtra = new ObservableValue(true)
|
|
173
|
+
|
|
174
|
+
const ExampleComponent = Shade({
|
|
175
|
+
shadowDomName: 'host-props-remove-attr-test',
|
|
176
|
+
render: ({ useHostProps, useObservable }) => {
|
|
177
|
+
const [show] = useObservable('showExtra', showExtra)
|
|
178
|
+
useHostProps({
|
|
179
|
+
'data-always': 'yes',
|
|
180
|
+
...(show ? { 'data-extra': 'present' } : {}),
|
|
181
|
+
})
|
|
182
|
+
return <div>content</div>
|
|
183
|
+
},
|
|
184
|
+
})
|
|
185
|
+
|
|
186
|
+
initializeShadeRoot({
|
|
187
|
+
injector,
|
|
188
|
+
rootElement,
|
|
189
|
+
jsxElement: <ExampleComponent />,
|
|
190
|
+
})
|
|
191
|
+
await flushUpdates()
|
|
192
|
+
|
|
193
|
+
const el = document.querySelector('host-props-remove-attr-test') as HTMLElement
|
|
194
|
+
expect(el.getAttribute('data-always')).toBe('yes')
|
|
195
|
+
expect(el.getAttribute('data-extra')).toBe('present')
|
|
196
|
+
|
|
197
|
+
showExtra.setValue(false)
|
|
198
|
+
await flushUpdates()
|
|
199
|
+
|
|
200
|
+
expect(el.getAttribute('data-always')).toBe('yes')
|
|
201
|
+
expect(el.getAttribute('data-extra')).toBeNull()
|
|
202
|
+
})
|
|
203
|
+
})
|
|
204
|
+
|
|
205
|
+
it('should remove CSS custom properties when they are no longer set', async () => {
|
|
206
|
+
await usingAsync(new Injector(), async (injector) => {
|
|
207
|
+
const rootElement = document.getElementById('root') as HTMLDivElement
|
|
208
|
+
const showColor = new ObservableValue(true)
|
|
209
|
+
|
|
210
|
+
const ExampleComponent = Shade({
|
|
211
|
+
shadowDomName: 'host-props-remove-css-var-test',
|
|
212
|
+
render: ({ useHostProps, useObservable }) => {
|
|
213
|
+
const [show] = useObservable('showColor', showColor)
|
|
214
|
+
useHostProps({
|
|
215
|
+
style: {
|
|
216
|
+
display: 'block',
|
|
217
|
+
...(show ? { '--my-color': 'red' } : {}),
|
|
218
|
+
},
|
|
219
|
+
})
|
|
220
|
+
return <div>content</div>
|
|
221
|
+
},
|
|
222
|
+
})
|
|
223
|
+
|
|
224
|
+
initializeShadeRoot({
|
|
225
|
+
injector,
|
|
226
|
+
rootElement,
|
|
227
|
+
jsxElement: <ExampleComponent />,
|
|
228
|
+
})
|
|
229
|
+
await flushUpdates()
|
|
230
|
+
|
|
231
|
+
const el = document.querySelector('host-props-remove-css-var-test') as HTMLElement
|
|
232
|
+
expect(el.style.getPropertyValue('--my-color')).toBe('red')
|
|
233
|
+
expect(el.style.display).toBe('block')
|
|
234
|
+
|
|
235
|
+
showColor.setValue(false)
|
|
236
|
+
await flushUpdates()
|
|
237
|
+
|
|
238
|
+
expect(el.style.getPropertyValue('--my-color')).toBe('')
|
|
239
|
+
expect(el.style.display).toBe('block')
|
|
240
|
+
})
|
|
241
|
+
})
|
|
242
|
+
|
|
243
|
+
it('should set event handlers on the host element', async () => {
|
|
244
|
+
await usingAsync(new Injector(), async (injector) => {
|
|
245
|
+
const rootElement = document.getElementById('root') as HTMLDivElement
|
|
246
|
+
let clicked = false
|
|
247
|
+
|
|
248
|
+
const ExampleComponent = Shade({
|
|
249
|
+
shadowDomName: 'host-props-event-test',
|
|
250
|
+
render: ({ useHostProps }) => {
|
|
251
|
+
useHostProps({
|
|
252
|
+
onclick: () => {
|
|
253
|
+
clicked = true
|
|
254
|
+
},
|
|
255
|
+
})
|
|
256
|
+
return <div>clickable</div>
|
|
257
|
+
},
|
|
258
|
+
})
|
|
259
|
+
|
|
260
|
+
initializeShadeRoot({
|
|
261
|
+
injector,
|
|
262
|
+
rootElement,
|
|
263
|
+
jsxElement: <ExampleComponent />,
|
|
264
|
+
})
|
|
265
|
+
await flushUpdates()
|
|
266
|
+
|
|
267
|
+
const el = document.querySelector('host-props-event-test') as HTMLElement
|
|
268
|
+
el.click()
|
|
269
|
+
expect(clicked).toBe(true)
|
|
270
|
+
})
|
|
271
|
+
})
|
|
272
|
+
})
|
|
273
|
+
|
|
274
|
+
describe('useRef integration tests', () => {
|
|
275
|
+
beforeEach(() => {
|
|
276
|
+
document.body.innerHTML = '<div id="root"></div>'
|
|
277
|
+
})
|
|
278
|
+
afterEach(() => {
|
|
279
|
+
document.body.innerHTML = ''
|
|
280
|
+
})
|
|
281
|
+
|
|
282
|
+
it('should set ref.current to the mounted element', async () => {
|
|
283
|
+
await usingAsync(new Injector(), async (injector) => {
|
|
284
|
+
const rootElement = document.getElementById('root') as HTMLDivElement
|
|
285
|
+
|
|
286
|
+
let capturedRef: { readonly current: HTMLDivElement | null } | undefined
|
|
287
|
+
|
|
288
|
+
const ExampleComponent = Shade({
|
|
289
|
+
shadowDomName: 'use-ref-basic-test',
|
|
290
|
+
render: ({ useRef }) => {
|
|
291
|
+
const divRef = useRef<HTMLDivElement>('myDiv')
|
|
292
|
+
capturedRef = divRef
|
|
293
|
+
return (
|
|
294
|
+
<div ref={divRef} id="target">
|
|
295
|
+
hello
|
|
296
|
+
</div>
|
|
297
|
+
)
|
|
298
|
+
},
|
|
299
|
+
})
|
|
300
|
+
|
|
301
|
+
initializeShadeRoot({
|
|
302
|
+
injector,
|
|
303
|
+
rootElement,
|
|
304
|
+
jsxElement: <ExampleComponent />,
|
|
305
|
+
})
|
|
306
|
+
await flushUpdates()
|
|
307
|
+
|
|
308
|
+
expect(capturedRef).toBeTruthy()
|
|
309
|
+
expect(capturedRef!.current).toBeTruthy()
|
|
310
|
+
expect(capturedRef!.current).toBe(document.getElementById('target'))
|
|
311
|
+
expect(capturedRef!.current?.textContent).toBe('hello')
|
|
312
|
+
})
|
|
313
|
+
})
|
|
314
|
+
|
|
315
|
+
it('should return the same ref object across re-renders', async () => {
|
|
316
|
+
await usingAsync(new Injector(), async (injector) => {
|
|
317
|
+
const rootElement = document.getElementById('root') as HTMLDivElement
|
|
318
|
+
const counter = new ObservableValue(0)
|
|
319
|
+
|
|
320
|
+
const capturedRefs: Array<{ readonly current: Element | null }> = []
|
|
321
|
+
|
|
322
|
+
const ExampleComponent = Shade({
|
|
323
|
+
shadowDomName: 'use-ref-stable-test',
|
|
324
|
+
render: ({ useRef, useObservable }) => {
|
|
325
|
+
const [count] = useObservable('counter', counter)
|
|
326
|
+
const divRef = useRef('myDiv')
|
|
327
|
+
capturedRefs.push(divRef)
|
|
328
|
+
return <div ref={divRef}>{count}</div>
|
|
329
|
+
},
|
|
330
|
+
})
|
|
331
|
+
|
|
332
|
+
initializeShadeRoot({
|
|
333
|
+
injector,
|
|
334
|
+
rootElement,
|
|
335
|
+
jsxElement: <ExampleComponent />,
|
|
336
|
+
})
|
|
337
|
+
await flushUpdates()
|
|
338
|
+
|
|
339
|
+
counter.setValue(1)
|
|
340
|
+
await flushUpdates()
|
|
341
|
+
|
|
342
|
+
counter.setValue(2)
|
|
343
|
+
await flushUpdates()
|
|
344
|
+
|
|
345
|
+
expect(capturedRefs.length).toBe(3)
|
|
346
|
+
expect(capturedRefs[0]).toBe(capturedRefs[1])
|
|
347
|
+
expect(capturedRefs[1]).toBe(capturedRefs[2])
|
|
348
|
+
})
|
|
349
|
+
})
|
|
350
|
+
|
|
351
|
+
it('should set ref.current on nested child elements', async () => {
|
|
352
|
+
await usingAsync(new Injector(), async (injector) => {
|
|
353
|
+
const rootElement = document.getElementById('root') as HTMLDivElement
|
|
354
|
+
|
|
355
|
+
let capturedInputRef: { readonly current: HTMLInputElement | null } | undefined
|
|
356
|
+
|
|
357
|
+
const ExampleComponent = Shade({
|
|
358
|
+
shadowDomName: 'use-ref-nested-test',
|
|
359
|
+
render: ({ useRef }) => {
|
|
360
|
+
const inputRef = useRef<HTMLInputElement>('input')
|
|
361
|
+
capturedInputRef = inputRef
|
|
362
|
+
return (
|
|
363
|
+
<div>
|
|
364
|
+
<label>
|
|
365
|
+
<input ref={inputRef} type="text" id="my-input" />
|
|
366
|
+
</label>
|
|
367
|
+
</div>
|
|
368
|
+
)
|
|
369
|
+
},
|
|
370
|
+
})
|
|
371
|
+
|
|
372
|
+
initializeShadeRoot({
|
|
373
|
+
injector,
|
|
374
|
+
rootElement,
|
|
375
|
+
jsxElement: <ExampleComponent />,
|
|
376
|
+
})
|
|
377
|
+
await flushUpdates()
|
|
378
|
+
|
|
379
|
+
expect(capturedInputRef).toBeTruthy()
|
|
380
|
+
expect(capturedInputRef!.current).toBeTruthy()
|
|
381
|
+
expect(capturedInputRef!.current).toBe(document.getElementById('my-input'))
|
|
382
|
+
expect(capturedInputRef!.current?.type).toBe('text')
|
|
383
|
+
})
|
|
384
|
+
})
|
|
385
|
+
|
|
386
|
+
it('should clear ref.current when element is unmounted', async () => {
|
|
387
|
+
await usingAsync(new Injector(), async (injector) => {
|
|
388
|
+
const rootElement = document.getElementById('root') as HTMLDivElement
|
|
389
|
+
const showChild = new ObservableValue(true)
|
|
390
|
+
|
|
391
|
+
let capturedRef: { readonly current: HTMLSpanElement | null } | undefined
|
|
392
|
+
|
|
393
|
+
const ExampleComponent = Shade({
|
|
394
|
+
shadowDomName: 'use-ref-unmount-test',
|
|
395
|
+
render: ({ useRef, useObservable }) => {
|
|
396
|
+
const [show] = useObservable('showChild', showChild)
|
|
397
|
+
const spanRef = useRef<HTMLSpanElement>('span')
|
|
398
|
+
capturedRef = spanRef
|
|
399
|
+
return <div>{show ? <span ref={spanRef}>visible</span> : null}</div>
|
|
400
|
+
},
|
|
401
|
+
})
|
|
402
|
+
|
|
403
|
+
initializeShadeRoot({
|
|
404
|
+
injector,
|
|
405
|
+
rootElement,
|
|
406
|
+
jsxElement: <ExampleComponent />,
|
|
407
|
+
})
|
|
408
|
+
await flushUpdates()
|
|
409
|
+
|
|
410
|
+
expect(capturedRef!.current).toBeTruthy()
|
|
411
|
+
expect(capturedRef!.current?.textContent).toBe('visible')
|
|
412
|
+
|
|
413
|
+
showChild.setValue(false)
|
|
414
|
+
await flushUpdates()
|
|
415
|
+
|
|
416
|
+
expect(capturedRef!.current).toBeNull()
|
|
417
|
+
})
|
|
418
|
+
})
|
|
419
|
+
|
|
420
|
+
it('should work with useRef in onChange callbacks', async () => {
|
|
421
|
+
await usingAsync(new Injector(), async (injector) => {
|
|
422
|
+
const rootElement = document.getElementById('root') as HTMLDivElement
|
|
423
|
+
const counter = new ObservableValue(0)
|
|
424
|
+
|
|
425
|
+
const ExampleComponent = Shade({
|
|
426
|
+
shadowDomName: 'use-ref-onchange-test',
|
|
427
|
+
render: ({ useRef, useObservable }) => {
|
|
428
|
+
const spanRef = useRef<HTMLSpanElement>('counterSpan')
|
|
429
|
+
useObservable('counter', counter, {
|
|
430
|
+
onChange: (value) => {
|
|
431
|
+
if (spanRef.current) {
|
|
432
|
+
spanRef.current.textContent = String(value)
|
|
433
|
+
}
|
|
434
|
+
},
|
|
435
|
+
})
|
|
436
|
+
return (
|
|
437
|
+
<span ref={spanRef} id="counter-span">
|
|
438
|
+
0
|
|
439
|
+
</span>
|
|
440
|
+
)
|
|
441
|
+
},
|
|
442
|
+
})
|
|
443
|
+
|
|
444
|
+
initializeShadeRoot({
|
|
445
|
+
injector,
|
|
446
|
+
rootElement,
|
|
447
|
+
jsxElement: <ExampleComponent />,
|
|
448
|
+
})
|
|
449
|
+
await flushUpdates()
|
|
450
|
+
|
|
451
|
+
const span = document.getElementById('counter-span')
|
|
452
|
+
expect(span?.textContent).toBe('0')
|
|
453
|
+
|
|
454
|
+
counter.setValue(42)
|
|
455
|
+
await flushUpdates()
|
|
456
|
+
|
|
457
|
+
expect(span?.textContent).toBe('42')
|
|
458
|
+
})
|
|
459
|
+
})
|
|
460
|
+
})
|