ripple 0.2.4 → 0.2.6
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/README.md +3 -380
- package/package.json +5 -2
- package/src/compiler/phases/1-parse/index.js +13 -4
- package/src/compiler/phases/2-analyze/index.js +7 -1
- package/src/compiler/phases/2-analyze/prune.js +232 -109
- package/src/compiler/phases/3-transform/index.js +44 -14
- package/src/compiler/phases/3-transform/segments.js +12 -9
- package/src/runtime/internal/client/blocks.js +27 -5
- package/src/runtime/internal/client/constants.js +1 -0
- package/src/runtime/internal/client/index.js +3 -4
- package/src/runtime/internal/client/render.js +32 -3
- package/src/runtime/internal/client/runtime.js +6 -1
- package/src/runtime/internal/client/utils.js +1 -0
- package/src/utils/builders.js +11 -0
- package/tests/__snapshots__/basic.test.ripple.snap +66 -0
- package/tests/basic.test.ripple +273 -0
- package/tests/use.test.ripple +32 -0
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
PAUSED,
|
|
9
9
|
RENDER_BLOCK,
|
|
10
10
|
ROOT_BLOCK,
|
|
11
|
-
TRY_BLOCK
|
|
11
|
+
TRY_BLOCK,
|
|
12
12
|
} from './constants';
|
|
13
13
|
import { next_sibling } from './operations';
|
|
14
14
|
import { apply_element_spread } from './render';
|
|
@@ -18,13 +18,15 @@ import {
|
|
|
18
18
|
active_reaction,
|
|
19
19
|
run_block,
|
|
20
20
|
run_teardown,
|
|
21
|
-
schedule_update
|
|
21
|
+
schedule_update,
|
|
22
22
|
} from './runtime';
|
|
23
23
|
import { suspend } from './try';
|
|
24
24
|
|
|
25
25
|
export function user_effect(fn) {
|
|
26
26
|
if (active_block === null) {
|
|
27
|
-
throw new Error(
|
|
27
|
+
throw new Error(
|
|
28
|
+
'effect() must be called within an active context, such as a component or effect'
|
|
29
|
+
);
|
|
28
30
|
}
|
|
29
31
|
|
|
30
32
|
var component = active_component;
|
|
@@ -33,7 +35,7 @@ export function user_effect(fn) {
|
|
|
33
35
|
e.push({
|
|
34
36
|
b: active_block,
|
|
35
37
|
fn,
|
|
36
|
-
r: active_reaction
|
|
38
|
+
r: active_reaction,
|
|
37
39
|
});
|
|
38
40
|
|
|
39
41
|
return;
|
|
@@ -66,6 +68,26 @@ export function async(fn) {
|
|
|
66
68
|
});
|
|
67
69
|
}
|
|
68
70
|
|
|
71
|
+
export function use(element, get_fn) {
|
|
72
|
+
var fn = undefined;
|
|
73
|
+
var e;
|
|
74
|
+
|
|
75
|
+
return block(RENDER_BLOCK, () => {
|
|
76
|
+
if (fn !== (fn = get_fn())) {
|
|
77
|
+
if (e) {
|
|
78
|
+
destroy_block(e);
|
|
79
|
+
e = null;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (fn) {
|
|
83
|
+
e = branch(() => {
|
|
84
|
+
effect(() => fn(element));
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
|
|
69
91
|
export function root(fn) {
|
|
70
92
|
return block(ROOT_BLOCK, fn);
|
|
71
93
|
}
|
|
@@ -97,7 +119,7 @@ export function block(flags, fn, state = null) {
|
|
|
97
119
|
p: active_block,
|
|
98
120
|
prev: null,
|
|
99
121
|
s: state,
|
|
100
|
-
t: null
|
|
122
|
+
t: null,
|
|
101
123
|
};
|
|
102
124
|
|
|
103
125
|
if (active_reaction !== null && (active_reaction.f & COMPUTED) !== 0) {
|
|
@@ -9,7 +9,7 @@ export {
|
|
|
9
9
|
set_selected,
|
|
10
10
|
} from './render.js';
|
|
11
11
|
|
|
12
|
-
export { render, render_spread, async } from './blocks.js';
|
|
12
|
+
export { render, render_spread, async, use } from './blocks.js';
|
|
13
13
|
|
|
14
14
|
export { event, delegate } from './events.js';
|
|
15
15
|
|
|
@@ -39,7 +39,8 @@ export {
|
|
|
39
39
|
structured_clone,
|
|
40
40
|
push_component,
|
|
41
41
|
pop_component,
|
|
42
|
-
untrack
|
|
42
|
+
untrack,
|
|
43
|
+
use_prop,
|
|
43
44
|
} from './runtime.js';
|
|
44
45
|
|
|
45
46
|
export { for_block as for } from './for.js';
|
|
@@ -49,5 +50,3 @@ export { if_block as if } from './if.js';
|
|
|
49
50
|
export { try_block as try, resume_context, aborted } from './try.js';
|
|
50
51
|
|
|
51
52
|
export { template, append } from './template.js';
|
|
52
|
-
|
|
53
|
-
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { destroy_block, use } from './blocks';
|
|
2
|
+
import { USE_PROP } from './constants';
|
|
3
|
+
import { get_descriptors, get_own_property_symbols, get_prototype_of } from './utils';
|
|
3
4
|
|
|
4
5
|
export function set_text(text, value) {
|
|
5
6
|
// For objects, we apply string coercion (which might make things like $state array references in the template reactive) before diffing
|
|
@@ -62,6 +63,8 @@ export function set_attribute(element, attribute, value) {
|
|
|
62
63
|
|
|
63
64
|
export function set_attributes(element, attributes) {
|
|
64
65
|
for (const key in attributes) {
|
|
66
|
+
if (key === '$children') continue;
|
|
67
|
+
|
|
65
68
|
let value = attributes[key];
|
|
66
69
|
|
|
67
70
|
if (key === 'class') {
|
|
@@ -158,7 +161,33 @@ export function set_selected(element, selected) {
|
|
|
158
161
|
}
|
|
159
162
|
|
|
160
163
|
export function apply_element_spread(element, fn) {
|
|
164
|
+
var prev;
|
|
165
|
+
var effects = {};
|
|
166
|
+
|
|
161
167
|
return () => {
|
|
162
|
-
|
|
168
|
+
var next = fn();
|
|
169
|
+
|
|
170
|
+
for (let symbol of get_own_property_symbols(effects)) {
|
|
171
|
+
if (!next[symbol]) {
|
|
172
|
+
destroy_block(effects[symbol]);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
for (const symbol of get_own_property_symbols(next)) {
|
|
177
|
+
var use_fn = next[symbol];
|
|
178
|
+
|
|
179
|
+
if (symbol.description === USE_PROP && (!prev || use_fn !== prev[symbol])) {
|
|
180
|
+
if (effects[symbol]) {
|
|
181
|
+
destroy_block(effects[symbol]);
|
|
182
|
+
}
|
|
183
|
+
effects[symbol] = use(element, use_fn);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
next[symbol] = use_fn;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
set_attributes(element, next);
|
|
190
|
+
|
|
191
|
+
prev = next;
|
|
163
192
|
};
|
|
164
193
|
}
|
|
@@ -22,7 +22,8 @@ import {
|
|
|
22
22
|
TRACKED,
|
|
23
23
|
TRACKED_OBJECT,
|
|
24
24
|
TRY_BLOCK,
|
|
25
|
-
UNINITIALIZED
|
|
25
|
+
UNINITIALIZED,
|
|
26
|
+
USE_PROP
|
|
26
27
|
} from './constants';
|
|
27
28
|
import { capture, suspend } from './try.js';
|
|
28
29
|
import { define_property, is_array } from './utils';
|
|
@@ -923,3 +924,7 @@ export function pop_component() {
|
|
|
923
924
|
}
|
|
924
925
|
active_component = component.p;
|
|
925
926
|
}
|
|
927
|
+
|
|
928
|
+
export function use_prop() {
|
|
929
|
+
return Symbol(USE_PROP);
|
|
930
|
+
}
|
|
@@ -7,6 +7,7 @@ export var get_prototype_of = Object.getPrototypeOf;
|
|
|
7
7
|
export var object_values = Object.values;
|
|
8
8
|
export var object_entries = Object.entries;
|
|
9
9
|
export var object_keys = Object.keys;
|
|
10
|
+
export var get_own_property_symbols = Object.getOwnPropertySymbols;
|
|
10
11
|
export var structured_clone = structuredClone;
|
|
11
12
|
|
|
12
13
|
export function create_anchor() {
|
package/src/utils/builders.js
CHANGED
|
@@ -716,6 +716,17 @@ export function jsx_id(name) {
|
|
|
716
716
|
};
|
|
717
717
|
}
|
|
718
718
|
|
|
719
|
+
/**
|
|
720
|
+
* @param {ESTree.Expression} argument
|
|
721
|
+
* @returns {ESTree.JSXSpreadAttribute}
|
|
722
|
+
*/
|
|
723
|
+
export function jsx_spread_attribute(argument) {
|
|
724
|
+
return {
|
|
725
|
+
type: 'JSXSpreadAttribute',
|
|
726
|
+
argument
|
|
727
|
+
};
|
|
728
|
+
}
|
|
729
|
+
|
|
719
730
|
export {
|
|
720
731
|
await_builder as await,
|
|
721
732
|
let_builder as let,
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
|
2
|
+
|
|
3
|
+
exports[`basic > render dynamic text 1`] = `
|
|
4
|
+
<div>
|
|
5
|
+
<button>
|
|
6
|
+
Change Text
|
|
7
|
+
</button>
|
|
8
|
+
<div>
|
|
9
|
+
Hello World
|
|
10
|
+
</div>
|
|
11
|
+
|
|
12
|
+
</div>
|
|
13
|
+
`;
|
|
14
|
+
|
|
15
|
+
exports[`basic > render semi-dynamic text 1`] = `
|
|
16
|
+
<div>
|
|
17
|
+
<div>
|
|
18
|
+
Hello World
|
|
19
|
+
</div>
|
|
20
|
+
|
|
21
|
+
</div>
|
|
22
|
+
`;
|
|
23
|
+
|
|
24
|
+
exports[`basic > render static attributes 1`] = `
|
|
25
|
+
<div>
|
|
26
|
+
<div
|
|
27
|
+
class="foo"
|
|
28
|
+
id="bar"
|
|
29
|
+
style="color: red;"
|
|
30
|
+
>
|
|
31
|
+
Hello World
|
|
32
|
+
</div>
|
|
33
|
+
|
|
34
|
+
</div>
|
|
35
|
+
`;
|
|
36
|
+
|
|
37
|
+
exports[`basic > render static elements 1`] = `
|
|
38
|
+
<div>
|
|
39
|
+
<div
|
|
40
|
+
class="foo"
|
|
41
|
+
id="bar"
|
|
42
|
+
style="color: red;"
|
|
43
|
+
>
|
|
44
|
+
Hello World
|
|
45
|
+
</div>
|
|
46
|
+
|
|
47
|
+
</div>
|
|
48
|
+
`;
|
|
49
|
+
|
|
50
|
+
exports[`basic > render static text 1`] = `
|
|
51
|
+
<div>
|
|
52
|
+
<div>
|
|
53
|
+
Hello World
|
|
54
|
+
</div>
|
|
55
|
+
|
|
56
|
+
</div>
|
|
57
|
+
`;
|
|
58
|
+
|
|
59
|
+
exports[`basic > renders correctly 1`] = `
|
|
60
|
+
<div>
|
|
61
|
+
<div>
|
|
62
|
+
Hello World
|
|
63
|
+
</div>
|
|
64
|
+
|
|
65
|
+
</div>
|
|
66
|
+
`;
|
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
2
|
+
import { mount, flushSync } from 'ripple';
|
|
3
|
+
|
|
4
|
+
describe('basic', () => {
|
|
5
|
+
let container;
|
|
6
|
+
|
|
7
|
+
function render(component) {
|
|
8
|
+
mount(component, { target: container });
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
beforeEach(() => {
|
|
12
|
+
container = document.createElement('div');
|
|
13
|
+
document.body.appendChild(container);
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
afterEach(() => {
|
|
17
|
+
document.body.removeChild(container);
|
|
18
|
+
container = null;
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it('render static text', () => {
|
|
22
|
+
component Basic() {
|
|
23
|
+
<div>{"Hello World"}</div>
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
render(Basic);
|
|
27
|
+
|
|
28
|
+
expect(container).toMatchSnapshot();
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it('render static attributes', () => {
|
|
32
|
+
component Basic() {
|
|
33
|
+
<div class="foo" id="bar" style="color: red;">{"Hello World"}</div>
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
render(Basic);
|
|
37
|
+
|
|
38
|
+
expect(container).toMatchSnapshot();
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it('render semi-dynamic text', () => {
|
|
42
|
+
component Basic() {
|
|
43
|
+
let text = 'Hello World';
|
|
44
|
+
<div>{text}</div>
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
render(Basic);
|
|
48
|
+
|
|
49
|
+
expect(container).toMatchSnapshot();
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it('render dynamic text', () => {
|
|
53
|
+
component Basic() {
|
|
54
|
+
let $text = 'Hello World';
|
|
55
|
+
|
|
56
|
+
<button onClick={() => $text = 'Hello Ripple'}>{"Change Text"}</button>
|
|
57
|
+
|
|
58
|
+
<div>{$text}</div>
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
render(Basic);
|
|
62
|
+
|
|
63
|
+
const button = container.querySelector('button');
|
|
64
|
+
|
|
65
|
+
button.click();
|
|
66
|
+
flushSync();
|
|
67
|
+
|
|
68
|
+
expect(container.querySelector('div').textContent).toEqual('Hello Ripple');
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it('render dynamic class attribute', () => {
|
|
72
|
+
component Basic() {
|
|
73
|
+
let $active = false;
|
|
74
|
+
|
|
75
|
+
<button onClick={() => $active = !$active}>{"Toggle"}</button>
|
|
76
|
+
|
|
77
|
+
<div $class={$active ? 'active' : 'inactive'}>{"Dynamic Class"}</div>
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
render(Basic);
|
|
81
|
+
|
|
82
|
+
const button = container.querySelector('button');
|
|
83
|
+
const div = container.querySelector('div');
|
|
84
|
+
|
|
85
|
+
expect(div.className).toBe('inactive');
|
|
86
|
+
|
|
87
|
+
button.click();
|
|
88
|
+
flushSync();
|
|
89
|
+
|
|
90
|
+
expect(div.className).toBe('active');
|
|
91
|
+
|
|
92
|
+
button.click();
|
|
93
|
+
flushSync();
|
|
94
|
+
|
|
95
|
+
expect(div.className).toBe('inactive');
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
it('render dynamic id attribute', () => {
|
|
99
|
+
component Basic() {
|
|
100
|
+
let $count = 0;
|
|
101
|
+
|
|
102
|
+
<button onClick={() => $count++}>{"Increment"}</button>
|
|
103
|
+
|
|
104
|
+
<div $id={`item-${$count}`}>{"Dynamic ID"}</div>
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
render(Basic);
|
|
108
|
+
|
|
109
|
+
const button = container.querySelector('button');
|
|
110
|
+
const div = container.querySelector('div');
|
|
111
|
+
|
|
112
|
+
expect(div.id).toBe('item-0');
|
|
113
|
+
|
|
114
|
+
button.click();
|
|
115
|
+
flushSync();
|
|
116
|
+
|
|
117
|
+
expect(div.id).toBe('item-1');
|
|
118
|
+
|
|
119
|
+
button.click();
|
|
120
|
+
flushSync();
|
|
121
|
+
|
|
122
|
+
expect(div.id).toBe('item-2');
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
it('render dynamic style attribute', () => {
|
|
126
|
+
component Basic() {
|
|
127
|
+
let $color = 'red';
|
|
128
|
+
|
|
129
|
+
<button onClick={() => $color = $color === 'red' ? 'blue' : 'red'}>{"Change Color"}</button>
|
|
130
|
+
|
|
131
|
+
<div $style={`color: ${$color}; font-weight: bold;`}>{"Dynamic Style"}</div>
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
render(Basic);
|
|
135
|
+
|
|
136
|
+
const button = container.querySelector('button');
|
|
137
|
+
const div = container.querySelector('div');
|
|
138
|
+
|
|
139
|
+
expect(div.style.color).toBe('red');
|
|
140
|
+
expect(div.style.fontWeight).toBe('bold');
|
|
141
|
+
|
|
142
|
+
button.click();
|
|
143
|
+
flushSync();
|
|
144
|
+
|
|
145
|
+
expect(div.style.color).toBe('blue');
|
|
146
|
+
expect(div.style.fontWeight).toBe('bold');
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
it('render dynamic boolean attributes', () => {
|
|
150
|
+
component Basic() {
|
|
151
|
+
let $disabled = false;
|
|
152
|
+
let $checked = false;
|
|
153
|
+
|
|
154
|
+
<button onClick={() => { $disabled = !$disabled; $checked = !$checked; }}>{"Toggle"}</button>
|
|
155
|
+
|
|
156
|
+
<input type="checkbox" $disabled={$disabled} $checked={$checked} />
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
render(Basic);
|
|
160
|
+
|
|
161
|
+
const button = container.querySelector('button');
|
|
162
|
+
const input = container.querySelector('input');
|
|
163
|
+
|
|
164
|
+
expect(input.disabled).toBe(false);
|
|
165
|
+
expect(input.checked).toBe(false);
|
|
166
|
+
|
|
167
|
+
button.click();
|
|
168
|
+
flushSync();
|
|
169
|
+
|
|
170
|
+
expect(input.disabled).toBe(true);
|
|
171
|
+
expect(input.checked).toBe(true);
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
it('render multiple dynamic attributes', () => {
|
|
175
|
+
component Basic() {
|
|
176
|
+
let $theme = 'light';
|
|
177
|
+
let $size = 'medium';
|
|
178
|
+
|
|
179
|
+
<button onClick={() => {
|
|
180
|
+
$theme = $theme === 'light' ? 'dark' : 'light';
|
|
181
|
+
$size = $size === 'medium' ? 'large' : 'medium';
|
|
182
|
+
}}>{"Toggle Theme & Size"}</button>
|
|
183
|
+
|
|
184
|
+
<div
|
|
185
|
+
$class={`theme-${$theme} size-${$size}`}
|
|
186
|
+
$data-theme={$theme}
|
|
187
|
+
$data-size={$size}
|
|
188
|
+
>
|
|
189
|
+
{"Multiple Dynamic Attributes"}
|
|
190
|
+
</div>
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
render(Basic);
|
|
194
|
+
|
|
195
|
+
const button = container.querySelector('button');
|
|
196
|
+
const div = container.querySelector('div');
|
|
197
|
+
|
|
198
|
+
expect(div.className).toBe('theme-light size-medium');
|
|
199
|
+
expect(div.getAttribute('data-theme')).toBe('light');
|
|
200
|
+
expect(div.getAttribute('data-size')).toBe('medium');
|
|
201
|
+
|
|
202
|
+
button.click();
|
|
203
|
+
flushSync();
|
|
204
|
+
|
|
205
|
+
expect(div.className).toBe('theme-dark size-large');
|
|
206
|
+
expect(div.getAttribute('data-theme')).toBe('dark');
|
|
207
|
+
expect(div.getAttribute('data-size')).toBe('large');
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
it('render conditional attributes', () => {
|
|
211
|
+
component Basic() {
|
|
212
|
+
let $showTitle = false;
|
|
213
|
+
let $showAria = false;
|
|
214
|
+
|
|
215
|
+
<button onClick={() => { $showTitle = !$showTitle; $showAria = !$showAria; }}>{"Toggle Attributes"}</button>
|
|
216
|
+
|
|
217
|
+
<div
|
|
218
|
+
$title={$showTitle ? 'This is a title' : null}
|
|
219
|
+
$aria-label={$showAria ? 'Accessible label' : null}
|
|
220
|
+
>
|
|
221
|
+
{"Conditional Attributes"}
|
|
222
|
+
</div>
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
render(Basic);
|
|
226
|
+
|
|
227
|
+
const button = container.querySelector('button');
|
|
228
|
+
const div = container.querySelector('div');
|
|
229
|
+
|
|
230
|
+
expect(div.hasAttribute('title')).toBe(false);
|
|
231
|
+
expect(div.hasAttribute('aria-label')).toBe(false);
|
|
232
|
+
|
|
233
|
+
button.click();
|
|
234
|
+
flushSync();
|
|
235
|
+
|
|
236
|
+
expect(div.getAttribute('title')).toBe('This is a title');
|
|
237
|
+
expect(div.getAttribute('aria-label')).toBe('Accessible label');
|
|
238
|
+
|
|
239
|
+
button.click();
|
|
240
|
+
flushSync();
|
|
241
|
+
|
|
242
|
+
expect(div.hasAttribute('title')).toBe(false);
|
|
243
|
+
expect(div.hasAttribute('aria-label')).toBe(false);
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
it('render spread attributes', () => {
|
|
247
|
+
component Basic() {
|
|
248
|
+
let $attrs = { class: 'initial', id: 'test-1' };
|
|
249
|
+
|
|
250
|
+
<button onClick={() => {
|
|
251
|
+
$attrs = { class: 'updated', id: 'test-2', 'data-extra': 'value' };
|
|
252
|
+
}}>{"Update Attributes"}</button>
|
|
253
|
+
|
|
254
|
+
<div {...$attrs}>{"Spread Attributes"}</div>
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
render(Basic);
|
|
258
|
+
|
|
259
|
+
const button = container.querySelector('button');
|
|
260
|
+
const div = container.querySelector('div');
|
|
261
|
+
|
|
262
|
+
expect(div.className).toBe('initial');
|
|
263
|
+
expect(div.id).toBe('test-1');
|
|
264
|
+
expect(div.hasAttribute('data-extra')).toBe(false);
|
|
265
|
+
|
|
266
|
+
button.click();
|
|
267
|
+
flushSync();
|
|
268
|
+
|
|
269
|
+
expect(div.className).toBe('updated');
|
|
270
|
+
expect(div.id).toBe('test-2');
|
|
271
|
+
expect(div.getAttribute('data-extra')).toBe('value');
|
|
272
|
+
});
|
|
273
|
+
});
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
2
|
+
import { mount, flushSync } from 'ripple';
|
|
3
|
+
|
|
4
|
+
describe('@use element decorators', () => {
|
|
5
|
+
let container;
|
|
6
|
+
|
|
7
|
+
function render(component) {
|
|
8
|
+
mount(component, { target: container });
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
beforeEach(() => {
|
|
12
|
+
container = document.createElement('div');
|
|
13
|
+
document.body.appendChild(container);
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
afterEach(() => {
|
|
17
|
+
document.body.removeChild(container);
|
|
18
|
+
container = null;
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it('capture a <div>', () => {
|
|
22
|
+
let div;
|
|
23
|
+
|
|
24
|
+
component Component() {
|
|
25
|
+
<div {@use (node) => { div = node; }}>{"Hello World"}</div>
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
render(Component);
|
|
29
|
+
|
|
30
|
+
expect(div.textContent).toBe('Hello World');
|
|
31
|
+
});
|
|
32
|
+
});
|