sprae 2.14.2 → 3.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/package.json +3 -4
- package/r&d.md +24 -24
- package/readme.md +5 -87
- package/sprae.js +216 -566
- package/sprae.min.js +1 -1
- package/src/core.js +12 -6
- package/src/directives.js +8 -5
- package/src/state.js +113 -0
- package/test/dom.js +935 -0
- package/test/index.html +2 -1
- package/test/state.js +274 -0
- package/test/test.js +2 -922
- package/todo.md +4 -4
package/test/dom.js
ADDED
|
@@ -0,0 +1,935 @@
|
|
|
1
|
+
// import { signal } from 'usignal/sync'
|
|
2
|
+
import { signal } from '@preact/signals-core'
|
|
3
|
+
import test, {is, any, throws} from 'tst'
|
|
4
|
+
import {tick, time} from 'wait-please'
|
|
5
|
+
import sprae from '../src/index.js'
|
|
6
|
+
import h from 'hyperf'
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
Object.defineProperty(DocumentFragment.prototype, 'outerHTML', {
|
|
10
|
+
get() {
|
|
11
|
+
let s = ''
|
|
12
|
+
this.childNodes.forEach(n => {
|
|
13
|
+
s += n.nodeType === 3 ? n.textContent : n.outerHTML != null ? n.outerHTML : ''
|
|
14
|
+
})
|
|
15
|
+
return s
|
|
16
|
+
}
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
test.skip('autoinit', async () => {
|
|
20
|
+
is(window.x.innerHTML, '1')
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
test('hidden: core', async () => {
|
|
24
|
+
let el = h`<div :hidden="hidden"></div>`
|
|
25
|
+
let params = sprae(el, {hidden:true})
|
|
26
|
+
is(el.outerHTML, `<div hidden=""></div>`)
|
|
27
|
+
params.hidden = false
|
|
28
|
+
is(el.outerHTML, `<div></div>`)
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
test.skip('hidden: reactive', async () => {
|
|
32
|
+
const hidden = signal(true)
|
|
33
|
+
let el = h`<div :hidden="hidden"></div>`
|
|
34
|
+
sprae(el, {hidden})
|
|
35
|
+
is(el.outerHTML, `<div hidden=""></div>`)
|
|
36
|
+
hidden.value = false
|
|
37
|
+
is(el.outerHTML, `<div></div>`)
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
test('common: reactive', async () => {
|
|
41
|
+
let el = h`<label :for="name" :text="name" ></label><input :id="name" :name="name" :type="name" :disabled="!name"/><a :href="url"></a><img :src="url"/>`
|
|
42
|
+
let params = sprae(el, {name:'text', url:'//google.com'})
|
|
43
|
+
is(el.outerHTML, `<label for="text">text</label><input id="text" name="text" type="text"><a href="//google.com"></a><img src="//google.com">`)
|
|
44
|
+
params.name = 'email'
|
|
45
|
+
is(el.outerHTML, `<label for="email">email</label><input id="email" name="email" type="email"><a href="//google.com"></a><img src="//google.com">`)
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
test('common: empty strings', async () => {
|
|
49
|
+
let el = h`<x :="" :x=""></x>`
|
|
50
|
+
sprae(el)
|
|
51
|
+
is(el.outerHTML, `<x></x>`)
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
test('common: comments', async () => {
|
|
55
|
+
let el = h`<x :="/* */" :x="/* */"></x>`
|
|
56
|
+
sprae(el)
|
|
57
|
+
is(el.outerHTML, `<x></x>`)
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
test('common: newlines', async () => {
|
|
61
|
+
let el = h`<x :text="
|
|
62
|
+
x
|
|
63
|
+
"></x>`
|
|
64
|
+
sprae(el, {x:1})
|
|
65
|
+
is(el.outerHTML, `<x>1</x>`)
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
test('common: const in on', async () => {
|
|
69
|
+
let el = h`<div :onx="() => {const x=1; y=x+1}"></div>`
|
|
70
|
+
let state = sprae(el, {y:0})
|
|
71
|
+
el.dispatchEvent(new CustomEvent('x'))
|
|
72
|
+
is(state.y, 2)
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
test('style', async () => {
|
|
76
|
+
let el = h`<x style="left: 1px" :style="style"></x>`
|
|
77
|
+
let params = sprae(el, {style: "top: 1px"})
|
|
78
|
+
is(el.outerHTML, `<x style="left: 1px; top: 1px"></x>`)
|
|
79
|
+
params.style = {top: '2px'}
|
|
80
|
+
is(el.outerHTML, `<x style="left: 1px; top: 2px;"></x>`)
|
|
81
|
+
|
|
82
|
+
params.style = {'--x': 123}
|
|
83
|
+
is(el.style.getPropertyValue('--x'), '123')
|
|
84
|
+
|
|
85
|
+
params.style = {top:'1px', bottom:'2px'}
|
|
86
|
+
is(el.outerHTML, `<x style="left: 1px; top: 1px; bottom: 2px;"></x>`)
|
|
87
|
+
|
|
88
|
+
params.style = {top:'2px', bottom: null}
|
|
89
|
+
// FIXME
|
|
90
|
+
is(el.outerHTML, `<x style="left: 1px; top: 2px;"></x>`)
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
test('class', async () => {
|
|
94
|
+
let el = h`<x class="base" :class="a"></x><y :class="[b, c]"></y><z :class="{b:true, c:d}"></z>`
|
|
95
|
+
const c = signal('z')
|
|
96
|
+
let params = sprae(el, {a:'x', b:'y', c, d:false});
|
|
97
|
+
is(el.outerHTML, `<x class="base x"></x><y class="y z"></y><z class="b"></z>`);
|
|
98
|
+
params.d = true;
|
|
99
|
+
is(el.outerHTML, `<x class="base x"></x><y class="y z"></y><z class="b c"></z>`);
|
|
100
|
+
// c.value = 'w'
|
|
101
|
+
// is(el.outerHTML, `<x class="base x"></x><y class="y w"></y><z class="b c"></z>`);
|
|
102
|
+
})
|
|
103
|
+
|
|
104
|
+
test('props: base', async () => {
|
|
105
|
+
let el = h`<input :id="0" :="{for:1, title:2, help:3, type:4, placeholder: 5, value: 6, aB: 8}" :value="7"/>`
|
|
106
|
+
let params = sprae(el)
|
|
107
|
+
is(el.outerHTML, `<input id="0" for="1" title="2" help="3" type="4" placeholder="5" value="7" a-b="8">`)
|
|
108
|
+
})
|
|
109
|
+
|
|
110
|
+
test('props: sets prop', async () => {
|
|
111
|
+
let el = h`<x :x="this.x=1" :y="this.y='abc'"></x>`
|
|
112
|
+
sprae(el)
|
|
113
|
+
is(el.x, 1)
|
|
114
|
+
is(el.y, 'abc')
|
|
115
|
+
})
|
|
116
|
+
|
|
117
|
+
test('props: multiprop', async () => {
|
|
118
|
+
let el = h`<input :id:name:for="0" />`
|
|
119
|
+
let params = sprae(el)
|
|
120
|
+
is(el.outerHTML, `<input id="0" name="0" for="0">`)
|
|
121
|
+
})
|
|
122
|
+
|
|
123
|
+
// FIXME: this must work without return
|
|
124
|
+
test('props: calculation', async () => {
|
|
125
|
+
let el = h`<x :x="let a = 5; return Array.from({length: x}, (_,i)=>i).join('')"></x>`
|
|
126
|
+
let state = sprae(el, {x:3});
|
|
127
|
+
is(el.outerHTML, `<x x="012"></x>`)
|
|
128
|
+
state.x = 4
|
|
129
|
+
is(el.outerHTML, `<x x="0123"></x>`)
|
|
130
|
+
})
|
|
131
|
+
|
|
132
|
+
test.todo('props: semicols in expression', async () => {
|
|
133
|
+
let el = h`<x :x="0; return Array.from({length: x}, (_,i)=>i).join('')"></x>`
|
|
134
|
+
let state = sprae(el, {x:3});
|
|
135
|
+
is(el.outerHTML, `<x x="012"></x>`)
|
|
136
|
+
state.x = 4
|
|
137
|
+
is(el.outerHTML, `<x x="0123"></x>`)
|
|
138
|
+
})
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
test('data: base', async () => {
|
|
142
|
+
let el = h`<input :data="{a:1, fooBar:2}"/>`
|
|
143
|
+
let params = sprae(el)
|
|
144
|
+
is(el.outerHTML, `<input data-a="1" data-foo-bar="2">`)
|
|
145
|
+
})
|
|
146
|
+
|
|
147
|
+
test('aria: base', async () => {
|
|
148
|
+
let el = h`<input type="text" id="jokes" role="combobox" :aria="{controls:'joketypes', autocomplete:'list', expanded:false, activeOption:'item1', activedescendant:'', xxx:null}"/>`
|
|
149
|
+
sprae(el)
|
|
150
|
+
is(el.outerHTML, `<input type="text" id="jokes" role="combobox" aria-controls="joketypes" aria-autocomplete="list" aria-expanded="false" aria-active-option="item1" aria-activedescendant="">`)
|
|
151
|
+
})
|
|
152
|
+
|
|
153
|
+
test('value: direct', async () => {
|
|
154
|
+
let el = h`<input :value="a" />`
|
|
155
|
+
let state = sprae(el, {a:1})
|
|
156
|
+
is(el.value, '1')
|
|
157
|
+
is(el.outerHTML, `<input value="1">`)
|
|
158
|
+
state.a = 2
|
|
159
|
+
is(el.value, '2')
|
|
160
|
+
is(el.outerHTML, `<input value="2">`)
|
|
161
|
+
|
|
162
|
+
el.value = 3
|
|
163
|
+
// el.dispatchEvent(new window.Event('change'))
|
|
164
|
+
// is(state.a, '3')
|
|
165
|
+
})
|
|
166
|
+
|
|
167
|
+
test('value: textarea', async () => {
|
|
168
|
+
let el = h`<textarea :value="a"></textarea>`
|
|
169
|
+
let state = sprae(el, {a: 'abcdefgh'})
|
|
170
|
+
is(el.selectionStart, 8)
|
|
171
|
+
is(el.selectionEnd, 8)
|
|
172
|
+
el.setSelectionRange(1, 4)
|
|
173
|
+
is(el.selectionStart, 1)
|
|
174
|
+
is(el.selectionEnd, 4)
|
|
175
|
+
state.a = 'xyzyvw'
|
|
176
|
+
is(el.selectionStart, 1)
|
|
177
|
+
is(el.selectionEnd, 4)
|
|
178
|
+
})
|
|
179
|
+
|
|
180
|
+
test('text: core', async () => {
|
|
181
|
+
let el = h`<div :text="text"></div>`
|
|
182
|
+
let params = sprae(el, {text:'abc'})
|
|
183
|
+
is(el.outerHTML, `<div>abc</div>`)
|
|
184
|
+
params.text = null
|
|
185
|
+
is(el.outerHTML, `<div></div>`)
|
|
186
|
+
})
|
|
187
|
+
|
|
188
|
+
test('if: base', async () => {
|
|
189
|
+
let el = h`<p>
|
|
190
|
+
<span :if="a==1">a</span>
|
|
191
|
+
<span :else :if="a==2">b</span>
|
|
192
|
+
<span :else >c</span>
|
|
193
|
+
</p>`
|
|
194
|
+
|
|
195
|
+
const params = sprae(el, { a: 1 })
|
|
196
|
+
|
|
197
|
+
is(el.innerHTML, '<span>a</span>')
|
|
198
|
+
params.a = 2
|
|
199
|
+
is(el.innerHTML, '<span>b</span>')
|
|
200
|
+
params.a = 3
|
|
201
|
+
is(el.innerHTML, '<span>c</span>')
|
|
202
|
+
params.a = null
|
|
203
|
+
is(el.innerHTML, '<span>c</span>')
|
|
204
|
+
})
|
|
205
|
+
|
|
206
|
+
test('if: short with insertions', async () => {
|
|
207
|
+
let el = h`<p>
|
|
208
|
+
<span :if="a==1" :text="'1:'+a"></span>
|
|
209
|
+
<span :else :if="a==2" :text="'2:'+a"></span>
|
|
210
|
+
<span :else :text="a"></span>
|
|
211
|
+
</p>`
|
|
212
|
+
|
|
213
|
+
const params = sprae(el, { a: 1 })
|
|
214
|
+
|
|
215
|
+
is(el.innerHTML, '<span>1:1</span>')
|
|
216
|
+
params.a = 2
|
|
217
|
+
is(el.innerHTML, '<span>2:2</span>')
|
|
218
|
+
params.a = 3
|
|
219
|
+
is(el.innerHTML, '<span>3</span>')
|
|
220
|
+
params.a = 4
|
|
221
|
+
is(el.innerHTML, '<span>4</span>')
|
|
222
|
+
|
|
223
|
+
params.a = 1
|
|
224
|
+
is(el.innerHTML, '<span>1:1</span>')
|
|
225
|
+
params.a = 4
|
|
226
|
+
is(el.innerHTML, '<span>4</span>')
|
|
227
|
+
|
|
228
|
+
params.a = null
|
|
229
|
+
})
|
|
230
|
+
|
|
231
|
+
test.skip('if: reactive values', async () => {
|
|
232
|
+
let el = h`<p>
|
|
233
|
+
<span :if="a==1" :text="'1:'+a"></span>
|
|
234
|
+
<span :else :if="a==2" :text="'2:'+a"></span>
|
|
235
|
+
<span :else :text="a"></span>
|
|
236
|
+
</p>`
|
|
237
|
+
|
|
238
|
+
const a = signal(1)
|
|
239
|
+
sprae(el, { a })
|
|
240
|
+
|
|
241
|
+
is(el.innerHTML, '<span>1:1</span>')
|
|
242
|
+
a.value = 2
|
|
243
|
+
is(el.innerHTML, '<span>2:2</span>')
|
|
244
|
+
a.value = 3
|
|
245
|
+
is(el.innerHTML, '<span>3</span>')
|
|
246
|
+
a.value = 4
|
|
247
|
+
is(el.innerHTML, '<span>4</span>')
|
|
248
|
+
|
|
249
|
+
a.value = 1
|
|
250
|
+
is(el.innerHTML, '<span>1:1</span>')
|
|
251
|
+
a.value = 4
|
|
252
|
+
is(el.innerHTML, '<span>4</span>')
|
|
253
|
+
})
|
|
254
|
+
|
|
255
|
+
test('if: (#3) subsequent content is not abandoned', async () => {
|
|
256
|
+
let x = h`<x><y :if="!!y"></y><z :text="123"></z></x>`
|
|
257
|
+
sprae(x, {y: false})
|
|
258
|
+
is(x.outerHTML, `<x><z>123</z></x>`)
|
|
259
|
+
})
|
|
260
|
+
|
|
261
|
+
test('if: + :with doesnt prevent secondary effects from happening', () => {
|
|
262
|
+
let el = h`<div><x :if="x" :with="{}" :text="x"></x></div>`
|
|
263
|
+
let state = sprae(el, {x:''})
|
|
264
|
+
is(el.innerHTML, ``)
|
|
265
|
+
state.x = '123'
|
|
266
|
+
is(el.innerHTML, `<x>123</x>`)
|
|
267
|
+
|
|
268
|
+
// NOTE: we ignore this case
|
|
269
|
+
// let el2 = h`<div><x :if="x" :with="{x:cond}" :text="x"></x></div>`
|
|
270
|
+
// let state2 = sprae(el, {cond:''})
|
|
271
|
+
// is(el2.innerHTML, ``)
|
|
272
|
+
// state2.cond = '123'
|
|
273
|
+
// is(el2.innerHTML, `<x>123</x>`)
|
|
274
|
+
})
|
|
275
|
+
|
|
276
|
+
test('each: array', async () => {
|
|
277
|
+
// FIXME: in some conspicuous reason jsdom fails to update text nodes somehow
|
|
278
|
+
let el = h`<p>
|
|
279
|
+
<span :each="a in b" :text="a"></span>
|
|
280
|
+
</p>`
|
|
281
|
+
|
|
282
|
+
const params = sprae(el, { b: [] })
|
|
283
|
+
|
|
284
|
+
is(el.innerHTML, '')
|
|
285
|
+
console.log('set 1,2')
|
|
286
|
+
params.b = [1,2]
|
|
287
|
+
is(el.innerHTML, '<span>1</span><span>2</span>')
|
|
288
|
+
params.b = []
|
|
289
|
+
is(el.innerHTML, '')
|
|
290
|
+
params.b = null
|
|
291
|
+
is(el.innerHTML, '')
|
|
292
|
+
})
|
|
293
|
+
|
|
294
|
+
test('each: object', async () => {
|
|
295
|
+
// FIXME: in some conspicuous reason jsdom fails to update text nodes somehow
|
|
296
|
+
let el = h`<p>
|
|
297
|
+
<span :each="x,key in b" :text="[key,x]"></span>
|
|
298
|
+
</p>`
|
|
299
|
+
|
|
300
|
+
const params = sprae(el, { b: null })
|
|
301
|
+
|
|
302
|
+
is(el.innerHTML, '')
|
|
303
|
+
console.log('set 1,2')
|
|
304
|
+
params.b = { x:1, y:2 }
|
|
305
|
+
is(el.innerHTML, '<span>x,1</span><span>y,2</span>')
|
|
306
|
+
params.b = []
|
|
307
|
+
is(el.innerHTML, '')
|
|
308
|
+
params.b = null
|
|
309
|
+
is(el.innerHTML, '')
|
|
310
|
+
})
|
|
311
|
+
|
|
312
|
+
test('each: loop within loop', async () => {
|
|
313
|
+
let el = h`<p>
|
|
314
|
+
<x :each="b in c"><y :each="a in b" :text="a"></y></x>
|
|
315
|
+
</p>`
|
|
316
|
+
|
|
317
|
+
const params = sprae(el, { c: [[1,2], [3,4]] })
|
|
318
|
+
|
|
319
|
+
is(el.innerHTML, '<x><y>1</y><y>2</y></x><x><y>3</y><y>4</y></x>')
|
|
320
|
+
params.c = [[5,6], [3,4]]
|
|
321
|
+
is(el.innerHTML, '<x><y>5</y><y>6</y></x><x><y>3</y><y>4</y></x>')
|
|
322
|
+
// params.c[1] = [7,8]
|
|
323
|
+
params.c = [params.c[0], [7,8]]
|
|
324
|
+
is(el.innerHTML, '<x><y>5</y><y>6</y></x><x><y>7</y><y>8</y></x>')
|
|
325
|
+
// is(el.innerHTML, '<span>1</span><span>2</span>')
|
|
326
|
+
params.c = []
|
|
327
|
+
is(el.innerHTML, '')
|
|
328
|
+
// params.b = null
|
|
329
|
+
// is(el.innerHTML, '')
|
|
330
|
+
})
|
|
331
|
+
|
|
332
|
+
test.skip('each: reactive values', async () => {
|
|
333
|
+
let el = h`<p>
|
|
334
|
+
<span :each="a in b" :text="a"></span>
|
|
335
|
+
</p>`
|
|
336
|
+
|
|
337
|
+
const b = signal([])
|
|
338
|
+
const params = sprae(el, { b })
|
|
339
|
+
|
|
340
|
+
is(el.innerHTML, '')
|
|
341
|
+
b.value = [1,2]
|
|
342
|
+
is(el.innerHTML, '<span>1</span><span>2</span>')
|
|
343
|
+
b.value = []
|
|
344
|
+
is(el.innerHTML, '')
|
|
345
|
+
params.b = null
|
|
346
|
+
is(el.innerHTML, '')
|
|
347
|
+
})
|
|
348
|
+
|
|
349
|
+
test('each: loop with condition', async () => {
|
|
350
|
+
// NOTE: there doesn't seem to be much value in exactly that
|
|
351
|
+
// also it creates confusion with :else directive
|
|
352
|
+
// prohibitin that allows in-order directives init
|
|
353
|
+
let el = h`<p>
|
|
354
|
+
<span :each="a in b" :text="a" :if="c"></span>
|
|
355
|
+
</p>`
|
|
356
|
+
|
|
357
|
+
const params = sprae(el, { b: [1,2], c: false })
|
|
358
|
+
|
|
359
|
+
is(el.innerHTML, '')
|
|
360
|
+
params.c = true
|
|
361
|
+
is(el.innerHTML, '<span>1</span><span>2</span>')
|
|
362
|
+
params.b = [1]
|
|
363
|
+
is(el.innerHTML, '<span>1</span>')
|
|
364
|
+
params.b = null
|
|
365
|
+
is(el.innerHTML, '')
|
|
366
|
+
})
|
|
367
|
+
|
|
368
|
+
test('each: condition with loop', async () => {
|
|
369
|
+
let el = h`<p>
|
|
370
|
+
<span :if="c" :each="a in b" :text="a"></span>
|
|
371
|
+
<span :else :text="c"></span>
|
|
372
|
+
</p>`
|
|
373
|
+
|
|
374
|
+
const params = sprae(el, { b: [1,2], c: false })
|
|
375
|
+
|
|
376
|
+
is(el.innerHTML, '<span>false</span>')
|
|
377
|
+
params.c = true
|
|
378
|
+
is(el.innerHTML, '<span>1</span><span>2</span>')
|
|
379
|
+
params.b = [1]
|
|
380
|
+
is(el.innerHTML, '<span>1</span>')
|
|
381
|
+
params.b = null
|
|
382
|
+
is(el.innerHTML, '')
|
|
383
|
+
console.log('c=false')
|
|
384
|
+
params.c = false
|
|
385
|
+
is(el.innerHTML, '<span>false</span>')
|
|
386
|
+
})
|
|
387
|
+
|
|
388
|
+
test('each: loop within condition', async () => {
|
|
389
|
+
let el = h`<p>
|
|
390
|
+
<x :if="a==1"><y :each="i in a" :text="i"></y></x>
|
|
391
|
+
<x :else :if="a==2"><y :each="i in a" :text="-i"></y></x>
|
|
392
|
+
</p>`
|
|
393
|
+
|
|
394
|
+
const params = sprae(el, { a: 1 })
|
|
395
|
+
|
|
396
|
+
is(el.innerHTML, '<x><y>1</y></x>')
|
|
397
|
+
params.a = 2
|
|
398
|
+
is(el.innerHTML, '<x><y>-1</y><y>-2</y></x>')
|
|
399
|
+
params.a = 0
|
|
400
|
+
is(el.innerHTML, '')
|
|
401
|
+
})
|
|
402
|
+
|
|
403
|
+
test('each: condition within loop', async () => {
|
|
404
|
+
let el = h`<p>
|
|
405
|
+
<x :each="a in b">
|
|
406
|
+
<y :if="a==1" :text="'1:'+a"></y>
|
|
407
|
+
<y :else :if="a==2" :text="'2:'+a"></y>
|
|
408
|
+
<y :else :text="a"></y>
|
|
409
|
+
</x>
|
|
410
|
+
</p>`
|
|
411
|
+
|
|
412
|
+
const params = sprae(el, { b: [1,2,3] })
|
|
413
|
+
|
|
414
|
+
is(el.innerHTML, '<x><y>1:1</y></x><x><y>2:2</y></x><x><y>3</y></x>')
|
|
415
|
+
params.b = [2]
|
|
416
|
+
is(el.innerHTML, '<x><y>2:2</y></x>')
|
|
417
|
+
params.b = null
|
|
418
|
+
is(el.innerHTML, '')
|
|
419
|
+
})
|
|
420
|
+
|
|
421
|
+
test('each: next items have own "this", not single one', async () => {
|
|
422
|
+
// FIXME: let el = h`<x :each="x in 3"></x>`
|
|
423
|
+
let el = h`<div><x :each="x in 3" :data="{x}" :x="log.push(x, this.dataset.x)"></x></div>`
|
|
424
|
+
let log = []
|
|
425
|
+
let state = sprae(el, {log})
|
|
426
|
+
is(state.log, [1,'1',2,'2',3,'3'])
|
|
427
|
+
})
|
|
428
|
+
|
|
429
|
+
test('each: unkeyed', async () => {
|
|
430
|
+
let el = h`<div><x :each="x in xs" :text="x"></x></div>`
|
|
431
|
+
let state = sprae(el, {xs:[1,2,3]})
|
|
432
|
+
is(el.children.length, 3)
|
|
433
|
+
is(el.textContent, '123')
|
|
434
|
+
// let first = el.firstChild
|
|
435
|
+
state.xs = [1,3,2]
|
|
436
|
+
// is(el.firstChild, first)
|
|
437
|
+
is(el.textContent, '132')
|
|
438
|
+
state.xs = [3,3,3]
|
|
439
|
+
is(el.textContent, '333')
|
|
440
|
+
// is(el.firstChild, first)
|
|
441
|
+
})
|
|
442
|
+
|
|
443
|
+
test('each: keyed', async () => {
|
|
444
|
+
// keyed
|
|
445
|
+
let el = h`<div><x :each="x in xs" :text="x" :key="x"></x></div>`
|
|
446
|
+
let state = sprae(el, {xs:[1,2,3]})
|
|
447
|
+
is(el.children.length, 3)
|
|
448
|
+
is(el.textContent, '123')
|
|
449
|
+
let first = el.firstChild
|
|
450
|
+
state.xs = [1,3,2]
|
|
451
|
+
is(el.firstChild, first)
|
|
452
|
+
is(el.textContent, '132')
|
|
453
|
+
state.xs = [3,3,3]
|
|
454
|
+
is(el.textContent, '3')
|
|
455
|
+
// is(el.firstChild, first)
|
|
456
|
+
})
|
|
457
|
+
|
|
458
|
+
test('each: wrapped source', async () => {
|
|
459
|
+
let el = h`<div><x :each="i in (x || 2)" :text="i"></x></div>`
|
|
460
|
+
sprae(el, {x:0})
|
|
461
|
+
is(el.innerHTML, `<x>1</x><x>2</x>`)
|
|
462
|
+
})
|
|
463
|
+
|
|
464
|
+
test('each: unmounted elements remove listeners', async () => {
|
|
465
|
+
// let's hope they get removed without memory leaks :')
|
|
466
|
+
})
|
|
467
|
+
|
|
468
|
+
test('each: internal children get updated by state update, also: update by running again', () => {
|
|
469
|
+
let el = h`<><x :each="item, idx in items" :text="item" :key="idx"></x></>`
|
|
470
|
+
let state = sprae(el, { items: [1,2,3] })
|
|
471
|
+
is(el.textContent, '123')
|
|
472
|
+
state.items = [2, 2, 3]
|
|
473
|
+
is(el.textContent, '223')
|
|
474
|
+
state = sprae(el, { items: [0,2,3] })
|
|
475
|
+
is(el.textContent, '023')
|
|
476
|
+
// NOTE: this doesn't update items, since they're new array
|
|
477
|
+
console.log('set items')
|
|
478
|
+
state.items[0] = 1
|
|
479
|
+
state.items = [...state.items]
|
|
480
|
+
is(el.textContent, '123')
|
|
481
|
+
})
|
|
482
|
+
|
|
483
|
+
test('each: :id and others must receive value from context', () => {
|
|
484
|
+
let el = h`<div><x :id="idx" :each="item, idx in items"></x></div>`
|
|
485
|
+
sprae(el, {items:[1,2,3]})
|
|
486
|
+
is(el.innerHTML,`<x id="1"></x><x id="2"></x><x id="3"></x>`)
|
|
487
|
+
})
|
|
488
|
+
|
|
489
|
+
test('each: key-based caching is in-sync with direct elements', () => {
|
|
490
|
+
let el = h`<ul><li :each="i in x" :key="i" :id="i"></li></ul>`
|
|
491
|
+
let el2 = h`<ul><li :each="i in x" :id="i"></li></ul>`
|
|
492
|
+
let state = sprae(el, {x:2})
|
|
493
|
+
let state2 = sprae(el2, {x:2})
|
|
494
|
+
is(el.outerHTML, el2.outerHTML)
|
|
495
|
+
el.firstChild.after(el.firstChild.cloneNode(true))
|
|
496
|
+
el2.firstChild.after(el2.firstChild.cloneNode(true))
|
|
497
|
+
state.x = 3
|
|
498
|
+
state2.x = 3
|
|
499
|
+
is(el.outerHTML, el2.outerHTML)
|
|
500
|
+
})
|
|
501
|
+
|
|
502
|
+
test('on: base', () => {
|
|
503
|
+
let el = h`<div :on="{click(e){log.push('click')}, x}"></div>`
|
|
504
|
+
let log = []
|
|
505
|
+
let params = sprae(el, {x(){log.push('x')}, log})
|
|
506
|
+
|
|
507
|
+
is(el.outerHTML, `<div></div>`);
|
|
508
|
+
el.dispatchEvent(new window.Event('click'));
|
|
509
|
+
is(log, ['click'])
|
|
510
|
+
el.dispatchEvent(new window.Event('x'));
|
|
511
|
+
is(log, ['click','x'])
|
|
512
|
+
|
|
513
|
+
params.x = function(){log.push('xx')}
|
|
514
|
+
el.dispatchEvent(new window.Event('x'));
|
|
515
|
+
is(log, ['click','x','xx']);
|
|
516
|
+
|
|
517
|
+
console.log('make null')
|
|
518
|
+
params.x = null;
|
|
519
|
+
el.dispatchEvent(new window.Event('x'));
|
|
520
|
+
is(log, ['click','x','xx']);
|
|
521
|
+
})
|
|
522
|
+
|
|
523
|
+
test('onevt: this context', e => {
|
|
524
|
+
let el = h`<div :onx="function(){log.push(this)}"></div>`
|
|
525
|
+
let state = sprae(el, {log: []})
|
|
526
|
+
el.dispatchEvent(new window.Event('x'));
|
|
527
|
+
is(state.log, [el])
|
|
528
|
+
})
|
|
529
|
+
|
|
530
|
+
test('on: this in chains refers to el', () => {
|
|
531
|
+
let el = h`<div :ona..onb="function(e){x=this; log.push(1); return () => log.push(2)}"></div>`
|
|
532
|
+
let state = sprae(el, {log:[], x:null})
|
|
533
|
+
el.dispatchEvent(new window.Event('a'));
|
|
534
|
+
is(state.log, [1])
|
|
535
|
+
})
|
|
536
|
+
|
|
537
|
+
test('on: multiple events', e => {
|
|
538
|
+
let el = h`<div :onscroll:onclick:onx="e=>log.push(e.type)"></div>`
|
|
539
|
+
let state = sprae(el, {log:[]})
|
|
540
|
+
|
|
541
|
+
el.dispatchEvent(new window.Event('click'));
|
|
542
|
+
is(state.log, ['click'])
|
|
543
|
+
el.dispatchEvent(new window.Event('scroll'));
|
|
544
|
+
is(state.log, ['click','scroll'])
|
|
545
|
+
el.dispatchEvent(new window.Event('x'));
|
|
546
|
+
is(state.log, ['click','scroll','x'])
|
|
547
|
+
})
|
|
548
|
+
|
|
549
|
+
test('on: in-out events', e => {
|
|
550
|
+
// let el = document.createElement('x');
|
|
551
|
+
// el.setAttribute(':onmousedown..onmouseup', 'e=>(log.push(e.type),e=>log.push(e.type))')
|
|
552
|
+
let el = h`<x :onmousedown..onmouseup="(e) => { x=this; log.push(e.type); return e=>log.push(e.type); }"></x>`
|
|
553
|
+
|
|
554
|
+
let state = sprae(el, {log:[],x:null})
|
|
555
|
+
el.dispatchEvent(new window.Event('mousedown'));
|
|
556
|
+
is(state.x, el);
|
|
557
|
+
is(state.log, ['mousedown'])
|
|
558
|
+
el.dispatchEvent(new window.Event('mouseup'));
|
|
559
|
+
is(state.log, ['mousedown','mouseup'])
|
|
560
|
+
})
|
|
561
|
+
|
|
562
|
+
test('on: in-out side-effects', e => {
|
|
563
|
+
let log = []
|
|
564
|
+
|
|
565
|
+
// 1. skip in event and do directly out
|
|
566
|
+
let el = h`<x :onin..onout="io"></x>`
|
|
567
|
+
sprae(el, { io(e) {
|
|
568
|
+
log.push(e.type)
|
|
569
|
+
return (e) => (log.push(e.type), [1,2,3])
|
|
570
|
+
} })
|
|
571
|
+
|
|
572
|
+
el.dispatchEvent(new window.Event('out'));
|
|
573
|
+
is(log, [])
|
|
574
|
+
|
|
575
|
+
// 2. Some nonsensical return is fine
|
|
576
|
+
el.dispatchEvent(new window.Event('in'));
|
|
577
|
+
is(log, ['in'])
|
|
578
|
+
el.dispatchEvent(new window.Event('out'));
|
|
579
|
+
is(log, ['in','out'], 'out triggers right')
|
|
580
|
+
el.dispatchEvent(new window.Event('out'));
|
|
581
|
+
is(log, ['in','out'])
|
|
582
|
+
el.dispatchEvent(new window.Event('in'));
|
|
583
|
+
is(log, ['in','out','in'])
|
|
584
|
+
el.dispatchEvent(new window.Event('in'));
|
|
585
|
+
is(log, ['in','out','in', 'in'])
|
|
586
|
+
el.dispatchEvent(new window.Event('out'));
|
|
587
|
+
is(log, ['in','out','in','in','out','out'])
|
|
588
|
+
el.dispatchEvent(new window.Event('out'));
|
|
589
|
+
is(log, ['in','out','in','in','out','out'])
|
|
590
|
+
})
|
|
591
|
+
|
|
592
|
+
test('on: chain of events', e => {
|
|
593
|
+
let el = h`<div :onmousedown..onmousemove..onmouseup="e=>(log.push(e.type),e=>(log.push(e.type),e=>log.push(e.type)))"></div>`
|
|
594
|
+
let state = sprae(el, {log:[]})
|
|
595
|
+
|
|
596
|
+
el.dispatchEvent(new window.Event('mousedown'));
|
|
597
|
+
is(state.log, ['mousedown'])
|
|
598
|
+
el.dispatchEvent(new window.Event('mousemove'));
|
|
599
|
+
is(state.log, ['mousedown','mousemove'])
|
|
600
|
+
el.dispatchEvent(new window.Event('mouseup'));
|
|
601
|
+
is(state.log, ['mousedown','mousemove','mouseup'])
|
|
602
|
+
el.dispatchEvent(new window.Event('mouseup'));
|
|
603
|
+
is(state.log, ['mousedown','mousemove','mouseup'])
|
|
604
|
+
el.dispatchEvent(new window.Event('mousedown'));
|
|
605
|
+
is(state.log, ['mousedown','mousemove','mouseup','mousedown'])
|
|
606
|
+
})
|
|
607
|
+
|
|
608
|
+
test('on: parallel chains', e => {
|
|
609
|
+
let el = h`<div :onx..ony..onz="e=>('x',log.push(e.type),e=>('y',log.push(e.type),e=>('z',log.push(e.type))))"></div>`
|
|
610
|
+
let state = sprae(el, {log:[]})
|
|
611
|
+
|
|
612
|
+
console.log('emit x')
|
|
613
|
+
el.dispatchEvent(new window.Event('x'));
|
|
614
|
+
is(state.log, ['x'])
|
|
615
|
+
console.log('emit x')
|
|
616
|
+
el.dispatchEvent(new window.Event('x'));
|
|
617
|
+
is(state.log, ['x','x'])
|
|
618
|
+
console.log('emit y')
|
|
619
|
+
el.dispatchEvent(new window.Event('y'));
|
|
620
|
+
is(state.log, ['x','x','y','y'])
|
|
621
|
+
console.log('emit y')
|
|
622
|
+
el.dispatchEvent(new window.Event('y'));
|
|
623
|
+
is(state.log, ['x','x','y','y'])
|
|
624
|
+
console.log('emit z')
|
|
625
|
+
el.dispatchEvent(new window.Event('z'));
|
|
626
|
+
console.log('emit y')
|
|
627
|
+
el.dispatchEvent(new window.Event('y'));
|
|
628
|
+
is(state.log, ['x','x','y','y','z','z'])
|
|
629
|
+
el.dispatchEvent(new window.Event('z'));
|
|
630
|
+
is(state.log, ['x','x','y','y','z','z']);
|
|
631
|
+
el.dispatchEvent(new window.Event('x'));
|
|
632
|
+
is(state.log, ['x','x','y','y','z','z','x']);
|
|
633
|
+
})
|
|
634
|
+
|
|
635
|
+
test('on: state changes between chain of events', e => {
|
|
636
|
+
let el = h`<x :on="{'x..y':fn}"></x>`
|
|
637
|
+
let log = []
|
|
638
|
+
let state = sprae(el, {log, fn: () => (log.push('x1'), ()=>log.push('y1'))})
|
|
639
|
+
console.log('emit x')
|
|
640
|
+
el.dispatchEvent(new window.Event('x'));
|
|
641
|
+
is(log, ['x1'])
|
|
642
|
+
console.log('update fn')
|
|
643
|
+
state.fn = () => (log.push('x2'), () => log.push('y2'))
|
|
644
|
+
is(log, ['x1'])
|
|
645
|
+
// console.log('xx')
|
|
646
|
+
// NOTE: state update registers new chain listener before finishing prev chain
|
|
647
|
+
// el.dispatchEvent(new window.Event('x'));
|
|
648
|
+
// el.dispatchEvent(new window.Event('x'));
|
|
649
|
+
// is(log, [1])
|
|
650
|
+
console.log('emit y, y')
|
|
651
|
+
el.dispatchEvent(new window.Event('y'));
|
|
652
|
+
el.dispatchEvent(new window.Event('y'));
|
|
653
|
+
is(log, ['x1', 'y1'])
|
|
654
|
+
console.log('emit x')
|
|
655
|
+
el.dispatchEvent(new window.Event('x'));
|
|
656
|
+
is(log, ['x1','y1','x2'])
|
|
657
|
+
console.log('emit y, y')
|
|
658
|
+
el.dispatchEvent(new window.Event('y'));
|
|
659
|
+
el.dispatchEvent(new window.Event('y'));
|
|
660
|
+
is(log, ['x1','y1','x2','y2'])
|
|
661
|
+
})
|
|
662
|
+
|
|
663
|
+
test('on: once', e => {
|
|
664
|
+
// NOTE: if callback updates it's still rebound
|
|
665
|
+
let el = h`<x :onx.once="(e=>x&&log.push(this))" ></x>`
|
|
666
|
+
let log = []
|
|
667
|
+
let state = sprae(el, {log, x:1})
|
|
668
|
+
el.dispatchEvent(new window.Event('x'));
|
|
669
|
+
is(log, [el])
|
|
670
|
+
el.dispatchEvent(new window.Event('x'));
|
|
671
|
+
is(log, [el])
|
|
672
|
+
state.x = 2
|
|
673
|
+
el.dispatchEvent(new window.Event('x'));
|
|
674
|
+
el.dispatchEvent(new window.Event('x'));
|
|
675
|
+
is(log, [el])
|
|
676
|
+
})
|
|
677
|
+
|
|
678
|
+
test('on: capture, stop, prevent', e => {
|
|
679
|
+
let el = h`<x :onx.capture="e=>log.push(1)"><y :onx="e=>log.push(2)"></y></x>`
|
|
680
|
+
let state = sprae(el, {log:[]})
|
|
681
|
+
el.firstChild.dispatchEvent(new window.Event('x', {bubbles:true}));
|
|
682
|
+
is(state.log, [1,2])
|
|
683
|
+
|
|
684
|
+
let el2 = h`<x :onx="e=>log.push(1)"><y :onx.stop="e=>log.push(2)"></y></x>`
|
|
685
|
+
let state2 = sprae(el2, {log:[]})
|
|
686
|
+
el2.firstChild.dispatchEvent(new window.Event('x', {bubbles:true}));
|
|
687
|
+
is(state2.log, [2])
|
|
688
|
+
})
|
|
689
|
+
|
|
690
|
+
test('on: window, self', e => {
|
|
691
|
+
let el = h`<x :onx.self="e=>log.push(1)"><y :onx.window="e=>log.push(2)"></y></x>`
|
|
692
|
+
let state = sprae(el, {log:[]})
|
|
693
|
+
el.firstChild.dispatchEvent(new window.Event('x', {bubbles:true}));
|
|
694
|
+
is(state.log, [])
|
|
695
|
+
el.dispatchEvent(new window.Event('x', {bubbles:true}));
|
|
696
|
+
is(state.log, [1])
|
|
697
|
+
window.dispatchEvent(new window.Event('x', {bubbles:true}));
|
|
698
|
+
is(state.log, [1,2])
|
|
699
|
+
})
|
|
700
|
+
|
|
701
|
+
test('on: keys', e => {
|
|
702
|
+
let el = h`<x :onkeydown.enter="e=>log.push(1)"></x>`
|
|
703
|
+
let state = {log:[]}
|
|
704
|
+
sprae(el, state)
|
|
705
|
+
el.dispatchEvent(new window.KeyboardEvent('keydown', { key: '' }));
|
|
706
|
+
is(state.log,[])
|
|
707
|
+
el.dispatchEvent(new window.KeyboardEvent('keydown', { key: 'Enter' }));
|
|
708
|
+
el.dispatchEvent(new window.KeyboardEvent('keydown', { key: '' }));
|
|
709
|
+
is(state.log,[1])
|
|
710
|
+
el.dispatchEvent(new window.KeyboardEvent('keydown', { key: 'Enter' }));
|
|
711
|
+
is(state.log,[1,1])
|
|
712
|
+
})
|
|
713
|
+
|
|
714
|
+
test('on: key combinations', e => {
|
|
715
|
+
let el = h`<x :onkeydown.ctrl-enter="e=>log.push(1)"></x>`
|
|
716
|
+
let state = {log:[]}
|
|
717
|
+
sprae(el, state)
|
|
718
|
+
el.dispatchEvent(new window.KeyboardEvent('keydown', { key: '' }));
|
|
719
|
+
is(state.log,[])
|
|
720
|
+
el.dispatchEvent(new window.KeyboardEvent('keydown', { key: 'Enter' }));
|
|
721
|
+
el.dispatchEvent(new window.KeyboardEvent('keydown', { key: '' }));
|
|
722
|
+
is(state.log,[])
|
|
723
|
+
el.dispatchEvent(new window.KeyboardEvent('keydown', { key: 'Enter', ctrlKey: true }));
|
|
724
|
+
el.dispatchEvent(new window.KeyboardEvent('keydown', { key: 'Enter' }));
|
|
725
|
+
is(state.log,[1])
|
|
726
|
+
el.dispatchEvent(new window.KeyboardEvent('keydown', { key: 'Enter' }));
|
|
727
|
+
el.dispatchEvent(new window.KeyboardEvent('keydown', { key: 'Enter', ctrlKey: true }));
|
|
728
|
+
is(state.log,[1,1])
|
|
729
|
+
let el2 = h`<x :onkeydown.ctrl-alt-enter="e=>log.push(1)"></x>`
|
|
730
|
+
})
|
|
731
|
+
|
|
732
|
+
test('on: keys with prevent', e => {
|
|
733
|
+
let el = h`<y :onkeydown="e=>log.push(e.key)"><x :ref="x" :onkeydown.enter.stop></x></y>`
|
|
734
|
+
let state = sprae(el, {log:[]})
|
|
735
|
+
state.x.dispatchEvent(new window.KeyboardEvent('keydown', { key: 'x', bubbles: true }));
|
|
736
|
+
console.log('enter')
|
|
737
|
+
state.x.dispatchEvent(new window.KeyboardEvent('keydown', { key: 'Enter', bubbles: true }));
|
|
738
|
+
is(state.log,['x'])
|
|
739
|
+
})
|
|
740
|
+
|
|
741
|
+
test('on: debounce', async e => {
|
|
742
|
+
let el = h`<x :onkeydown.debounce-1="e=>log.push(e.key)"></x>`
|
|
743
|
+
let state = sprae(el, {log:[]})
|
|
744
|
+
el.dispatchEvent(new window.KeyboardEvent('keydown', { key: 'x', bubbles: true }));
|
|
745
|
+
is(state.log, [])
|
|
746
|
+
await time(2)
|
|
747
|
+
is(state.log, ['x'])
|
|
748
|
+
})
|
|
749
|
+
|
|
750
|
+
test('on: debounce 0', async e => {
|
|
751
|
+
let el = h`<x :onkeydown.debounce-0="e=>log.push(e.key)"></x>`
|
|
752
|
+
let state = sprae(el, {log:[]})
|
|
753
|
+
el.dispatchEvent(new window.KeyboardEvent('keydown', { key: 'x', bubbles: true }));
|
|
754
|
+
is(state.log, [])
|
|
755
|
+
await time(2)
|
|
756
|
+
is(state.log, ['x'])
|
|
757
|
+
})
|
|
758
|
+
|
|
759
|
+
test('on: throttle', async e => {
|
|
760
|
+
let el = h`<x :onkeydown.throttle-10="e=>log.push(e.key)"></x>`
|
|
761
|
+
let state = sprae(el, {log:[]})
|
|
762
|
+
el.dispatchEvent(new window.KeyboardEvent('keydown', { key: 'x', bubbles: true }));
|
|
763
|
+
el.dispatchEvent(new window.KeyboardEvent('keydown', { key: 'x', bubbles: true }));
|
|
764
|
+
is(state.log, ['x'])
|
|
765
|
+
await time(5)
|
|
766
|
+
el.dispatchEvent(new window.KeyboardEvent('keydown', { key: 'x', bubbles: true }));
|
|
767
|
+
is(state.log, ['x'])
|
|
768
|
+
await time(10)
|
|
769
|
+
is(state.log, ['x', 'x'])
|
|
770
|
+
await time(10)
|
|
771
|
+
is(state.log, ['x', 'x'])
|
|
772
|
+
el.dispatchEvent(new window.KeyboardEvent('keydown', { key: 'x', bubbles: true }));
|
|
773
|
+
is(state.log, ['x', 'x', 'x'])
|
|
774
|
+
})
|
|
775
|
+
|
|
776
|
+
test('on: toggle', async e => {
|
|
777
|
+
let el = h`<x :onx.toggle="e=>(log.push(1),e=>log.push(2))"></x>`
|
|
778
|
+
let state = sprae(el, {log:[]})
|
|
779
|
+
el.dispatchEvent(new window.KeyboardEvent('x'));
|
|
780
|
+
is(state.log, [1])
|
|
781
|
+
el.dispatchEvent(new window.KeyboardEvent('x'));
|
|
782
|
+
is(state.log, [1,2])
|
|
783
|
+
el.dispatchEvent(new window.KeyboardEvent('x'));
|
|
784
|
+
is(state.log, [1,2,1])
|
|
785
|
+
el.dispatchEvent(new window.KeyboardEvent('x'));
|
|
786
|
+
is(state.log, [1,2,1,2])
|
|
787
|
+
})
|
|
788
|
+
|
|
789
|
+
test.skip('on: nexttick', async e => {
|
|
790
|
+
let el = h`<x :onkeydown.nexttick="e=>log.push(e.key)"></x>`
|
|
791
|
+
let state = sprae(el, {log:[]})
|
|
792
|
+
el.dispatchEvent(new window.KeyboardEvent('keydown', { key: 'x', bubbles: true }));
|
|
793
|
+
el.dispatchEvent(new window.KeyboardEvent('keydown', { key: 'x', bubbles: true }));
|
|
794
|
+
is(state.log, [])
|
|
795
|
+
await time()
|
|
796
|
+
is(state.log, ['x', 'x'])
|
|
797
|
+
})
|
|
798
|
+
|
|
799
|
+
test('on: modifiers chain', async e => {
|
|
800
|
+
let el = h`<x :onkeydown.letter..onkeyup.letter="e=>(log.push(e.key),(e)=>log.push(e.key))"></x>`
|
|
801
|
+
let state = sprae(el, {log:[]})
|
|
802
|
+
el.dispatchEvent(new window.KeyboardEvent('keydown', { key: 'x', bubbles: true }));
|
|
803
|
+
el.dispatchEvent(new window.KeyboardEvent('keydown', { key: 'Enter', bubbles: true }));
|
|
804
|
+
is(state.log,['x'])
|
|
805
|
+
el.dispatchEvent(new window.KeyboardEvent('keyup', { key: 'Enter', bubbles: true }));
|
|
806
|
+
is(state.log,['x'])
|
|
807
|
+
el.dispatchEvent(new window.KeyboardEvent('keyup', { key: 'x', bubbles: true }));
|
|
808
|
+
is(state.log,['x', 'x'])
|
|
809
|
+
})
|
|
810
|
+
|
|
811
|
+
test('with: inline', () => {
|
|
812
|
+
let el = h`<x :with="{foo:'bar'}"><y :text="foo + baz"></y></x>`
|
|
813
|
+
let state = sprae(el, {baz: 'qux'})
|
|
814
|
+
// FIXME: this doesn't inherit root scope baz property and instead uses hard-initialized one
|
|
815
|
+
is(el.innerHTML, `<y>barqux</y>`)
|
|
816
|
+
state.baz = 'quux'
|
|
817
|
+
is(el.innerHTML, `<y>barquux</y>`)
|
|
818
|
+
})
|
|
819
|
+
test.skip('with: inline reactive', () => {
|
|
820
|
+
let el = h`<x :with="{foo:'bar'}"><y :text="foo + baz"></y></x>`
|
|
821
|
+
let baz = signal('qux')
|
|
822
|
+
sprae(el, {baz})
|
|
823
|
+
// FIXME: this doesn't inherit root scope baz property and instead uses hard-initialized one
|
|
824
|
+
is(el.innerHTML, `<y>barqux</y>`)
|
|
825
|
+
baz.value = 'quux'
|
|
826
|
+
is(el.innerHTML, `<y>barquux</y>`)
|
|
827
|
+
})
|
|
828
|
+
test('with: data', () => {
|
|
829
|
+
let el = h`<x :with="x"><y :text="foo"></y></x>`
|
|
830
|
+
let state = sprae(el, {x: {foo:'bar'}})
|
|
831
|
+
is(el.innerHTML, `<y>bar</y>`)
|
|
832
|
+
console.log('update', state.x)
|
|
833
|
+
state.x.foo = 'baz'
|
|
834
|
+
// Object.assign(state, {x:{foo:'baz'}})
|
|
835
|
+
is(el.innerHTML, `<y>baz</y>`)
|
|
836
|
+
})
|
|
837
|
+
test('with: transparency', () => {
|
|
838
|
+
// NOTE: y:text initializes through directive, not through parent
|
|
839
|
+
// therefore by default :text uses parent's state, not defined by element itself
|
|
840
|
+
let el = h`<x :with="{foo:'foo'}"><y :with="b" :text="foo+bar"></y></x>`
|
|
841
|
+
let params = sprae(el, {b:{bar:'bar'}})
|
|
842
|
+
is(el.innerHTML, `<y>foobar</y>`)
|
|
843
|
+
params.b.bar = 'baz'
|
|
844
|
+
is(el.innerHTML, `<y>foobaz</y>`)
|
|
845
|
+
})
|
|
846
|
+
test.skip('with: reactive transparency', () => {
|
|
847
|
+
let el = h`<x :with="{foo:1}"><y :with="b.c" :text="foo+bar"></y></x>`
|
|
848
|
+
const bar = signal('2')
|
|
849
|
+
sprae(el, {b:{c:{bar}}})
|
|
850
|
+
is(el.innerHTML, `<y>12</y>`)
|
|
851
|
+
bar.value = '3'
|
|
852
|
+
is(el.innerHTML, `<y>13</y>`)
|
|
853
|
+
})
|
|
854
|
+
test('with: writes to state', () => {
|
|
855
|
+
let a = h`<x :with="{a:1}"><y :on="{x(){a++}}" :text="a"></y></x>`
|
|
856
|
+
sprae(a)
|
|
857
|
+
is(a.innerHTML, `<y>1</y>`)
|
|
858
|
+
a.firstChild.dispatchEvent(new window.Event('x'))
|
|
859
|
+
is(a.innerHTML, `<y>2</y>`)
|
|
860
|
+
a.firstChild.dispatchEvent(new window.Event('x'))
|
|
861
|
+
is(a.innerHTML, `<y>3</y>`)
|
|
862
|
+
})
|
|
863
|
+
|
|
864
|
+
test('ref: base', () => {
|
|
865
|
+
let a = h`<a :ref="a" :init="log.push(a), null" :text="b"></a>`
|
|
866
|
+
let state = sprae(a, {log:[], b:1})
|
|
867
|
+
is(state.log[0], a)
|
|
868
|
+
is(a.outerHTML, `<a>1</a>`)
|
|
869
|
+
state.b = 2
|
|
870
|
+
is(a.outerHTML, `<a>2</a>`)
|
|
871
|
+
is(state.a, a, 'Exposes to the state');
|
|
872
|
+
})
|
|
873
|
+
|
|
874
|
+
test('ref: with :each', () => {
|
|
875
|
+
let a = h`<y><x :ref="x" :each="item in items" :text="log.push(x), item"/></y>`
|
|
876
|
+
let state = sprae(a, {log: [], items: [1,2]})
|
|
877
|
+
is(a.innerHTML, `<x>1</x><x>2</x>`)
|
|
878
|
+
is(state.log, [...a.children])
|
|
879
|
+
})
|
|
880
|
+
|
|
881
|
+
test.skip(':: reactive values', async () => {
|
|
882
|
+
let a = new Promise((ok) => setTimeout(() => ok(2), 10))
|
|
883
|
+
|
|
884
|
+
let el = h`<x :text="a">1</x>`
|
|
885
|
+
sprae(el, {a})
|
|
886
|
+
is(el.outerHTML, `<x></x>`)
|
|
887
|
+
|
|
888
|
+
await time(20)
|
|
889
|
+
is(el.outerHTML, `<x>2</x>`)
|
|
890
|
+
})
|
|
891
|
+
|
|
892
|
+
test(':: null result does nothing', async () => {
|
|
893
|
+
let a = h`<x :="undefined"></x>`
|
|
894
|
+
sprae(a)
|
|
895
|
+
is(a.outerHTML, `<x></x>`)
|
|
896
|
+
})
|
|
897
|
+
|
|
898
|
+
test(':: scope refers to current element', async () => {
|
|
899
|
+
let el = h`<x :text="log.push(this)"></x>`
|
|
900
|
+
let state = sprae(el, {log:[]})
|
|
901
|
+
is(state.log, [el])
|
|
902
|
+
})
|
|
903
|
+
|
|
904
|
+
test(':: scope directives must come first', async () => {
|
|
905
|
+
// NOTE: we init attributes in order of definition
|
|
906
|
+
let a = h`<x :text="y" :with="{y:1}" :ref="x"></x>`
|
|
907
|
+
sprae(a, {})
|
|
908
|
+
is(a.outerHTML, `<x>1</x>`)
|
|
909
|
+
})
|
|
910
|
+
|
|
911
|
+
test.skip('getters', async () => {
|
|
912
|
+
let x = h`<x>
|
|
913
|
+
<h2 :if="doubledCount > 10">YAY!</h2>
|
|
914
|
+
<button :text="count" :on="{click:increment}"/>
|
|
915
|
+
<button :text="doubledCount" :on="{click:increment}"/>
|
|
916
|
+
</x>`
|
|
917
|
+
document.body.appendChild(x)
|
|
918
|
+
let state = sprae(x, {
|
|
919
|
+
count:0,
|
|
920
|
+
get doubledCount(){ console.log(this); return this.count * 2},
|
|
921
|
+
increment(){ this.count++ }
|
|
922
|
+
})
|
|
923
|
+
})
|
|
924
|
+
|
|
925
|
+
test('sandbox', async () => {
|
|
926
|
+
let el = h`<x :x="log.push(typeof window, typeof console, typeof arguments, typeof __scope)"></x>`
|
|
927
|
+
let log = []
|
|
928
|
+
sprae(el.cloneNode(), {log})
|
|
929
|
+
is(log, ['undefined', 'object', 'undefined', 'undefined'])
|
|
930
|
+
|
|
931
|
+
log.splice(0)
|
|
932
|
+
Object.assign(sprae.globals, { window })
|
|
933
|
+
sprae(el.cloneNode(), {log})
|
|
934
|
+
is(log, ['object', 'object', 'undefined', 'undefined'])
|
|
935
|
+
})
|