ripple 0.2.105 → 0.2.106
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 +1 -1
- package/src/compiler/phases/1-parse/index.js +18 -4
- package/src/compiler/phases/2-analyze/index.js +45 -4
- package/src/compiler/phases/3-transform/client/index.js +81 -31
- package/src/compiler/utils.js +1 -1
- package/src/runtime/create-subscriber.js +34 -0
- package/src/runtime/index-client.js +8 -0
- package/src/runtime/index-server.js +27 -2
- package/src/runtime/internal/client/constants.js +13 -12
- package/src/runtime/internal/client/index.js +2 -0
- package/src/runtime/internal/client/runtime.js +19 -19
- package/src/runtime/internal/client/switch.js +32 -0
- package/src/runtime/media-query.js +39 -0
- package/src/runtime/reactive-value.js +21 -0
- package/src/runtime/url-search-params.js +147 -0
- package/src/runtime/url.js +164 -0
- package/src/utils/builders.js +33 -0
- package/tests/client/__snapshots__/compiler.test.ripple.snap +12 -0
- package/tests/client/basic.test.ripple +46 -1
- package/tests/client/compiler.test.ripple +33 -0
- package/tests/client/media-query.test.ripple +130 -0
- package/tests/client/url-search-params.test.ripple +912 -0
- package/tests/client/url.test.ripple +954 -0
- package/types/index.d.ts +41 -11
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import { get, increment, safe_scope, tracked } from './internal/client/runtime.js';
|
|
2
|
+
import { get_current_url } from './url.js';
|
|
3
|
+
|
|
4
|
+
export const REPLACE = Symbol();
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
export class TrackedURLSearchParams extends URLSearchParams {
|
|
8
|
+
#block = safe_scope();
|
|
9
|
+
#version = tracked(0, this.#block);
|
|
10
|
+
#url = get_current_url();
|
|
11
|
+
|
|
12
|
+
#updating = false;
|
|
13
|
+
|
|
14
|
+
#update_url() {
|
|
15
|
+
if (!this.#url || this.#updating) return;
|
|
16
|
+
this.#updating = true;
|
|
17
|
+
|
|
18
|
+
const search = this.toString();
|
|
19
|
+
this.#url.search = search && `?${search}`;
|
|
20
|
+
|
|
21
|
+
this.#updating = false;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* @param {URLSearchParams} params
|
|
26
|
+
* @internal
|
|
27
|
+
*/
|
|
28
|
+
[REPLACE](params) {
|
|
29
|
+
if (this.#updating) return;
|
|
30
|
+
this.#updating = true;
|
|
31
|
+
|
|
32
|
+
for (const key of [...super.keys()]) {
|
|
33
|
+
super.delete(key);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
for (const [key, value] of params) {
|
|
37
|
+
super.append(key, value);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
increment(this.#version, this.#block);
|
|
41
|
+
this.#updating = false;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* @param {string} name
|
|
46
|
+
* @param {string} value
|
|
47
|
+
* @returns {void}
|
|
48
|
+
*/
|
|
49
|
+
append(name, value) {
|
|
50
|
+
super.append(name, value);
|
|
51
|
+
this.#update_url();
|
|
52
|
+
increment(this.#version, this.#block);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* @param {string} name
|
|
57
|
+
* @param {string=} value
|
|
58
|
+
* @returns {void}
|
|
59
|
+
*/
|
|
60
|
+
delete(name, value) {
|
|
61
|
+
var has_value = super.has(name, value);
|
|
62
|
+
super.delete(name, value);
|
|
63
|
+
if (has_value) {
|
|
64
|
+
this.#update_url();
|
|
65
|
+
increment(this.#version, this.#block);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* @param {string} name
|
|
71
|
+
* @returns {string|null}
|
|
72
|
+
*/
|
|
73
|
+
get(name) {
|
|
74
|
+
get(this.#version);
|
|
75
|
+
return super.get(name);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* @param {string} name
|
|
80
|
+
* @returns {string[]}
|
|
81
|
+
*/
|
|
82
|
+
getAll(name) {
|
|
83
|
+
get(this.#version);
|
|
84
|
+
return super.getAll(name);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* @param {string} name
|
|
89
|
+
* @param {string=} value
|
|
90
|
+
* @returns {boolean}
|
|
91
|
+
*/
|
|
92
|
+
has(name, value) {
|
|
93
|
+
get(this.#version);
|
|
94
|
+
return super.has(name, value);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
keys() {
|
|
98
|
+
get(this.#version);
|
|
99
|
+
return super.keys();
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* @param {string} name
|
|
104
|
+
* @param {string} value
|
|
105
|
+
* @returns {void}
|
|
106
|
+
*/
|
|
107
|
+
set(name, value) {
|
|
108
|
+
var previous = super.getAll(name).join('');
|
|
109
|
+
super.set(name, value);
|
|
110
|
+
// can't use has(name, value), because for something like https://svelte.dev?foo=1&bar=2&foo=3
|
|
111
|
+
// if you set `foo` to 1, then foo=3 gets deleted whilst `has("foo", "1")` returns true
|
|
112
|
+
if (previous !== super.getAll(name).join('')) {
|
|
113
|
+
this.#update_url();
|
|
114
|
+
increment(this.#version, this.#block);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
sort() {
|
|
119
|
+
super.sort();
|
|
120
|
+
this.#update_url();
|
|
121
|
+
increment(this.#version, this.#block);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
toString() {
|
|
125
|
+
get(this.#version);
|
|
126
|
+
return super.toString();
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
values() {
|
|
130
|
+
get(this.#version);
|
|
131
|
+
return super.values();
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
entries() {
|
|
135
|
+
get(this.#version);
|
|
136
|
+
return super.entries();
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
[Symbol.iterator]() {
|
|
140
|
+
return this.entries();
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
get size() {
|
|
144
|
+
get(this.#version);
|
|
145
|
+
return super.size;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import { get, set, safe_scope, tracked } from './internal/client/runtime.js';
|
|
2
|
+
import { REPLACE, TrackedURLSearchParams } from './url-search-params.js';
|
|
3
|
+
|
|
4
|
+
/** @type {TrackedURL | null} */
|
|
5
|
+
let current_url = null;
|
|
6
|
+
|
|
7
|
+
export function get_current_url() {
|
|
8
|
+
return current_url;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export class TrackedURL extends URL {
|
|
12
|
+
#block = safe_scope();
|
|
13
|
+
#protocol = tracked(super.protocol, this.#block);
|
|
14
|
+
#username = tracked(super.username, this.#block);
|
|
15
|
+
#password = tracked(super.password, this.#block);
|
|
16
|
+
#hostname = tracked(super.hostname, this.#block);
|
|
17
|
+
#port = tracked(super.port, this.#block);
|
|
18
|
+
#pathname = tracked(super.pathname, this.#block);
|
|
19
|
+
#hash = tracked(super.hash, this.#block);
|
|
20
|
+
#search = tracked(super.search, this.#block);
|
|
21
|
+
#searchParams;
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* @param {string | URL} url
|
|
25
|
+
* @param {string | URL} [base]
|
|
26
|
+
*/
|
|
27
|
+
constructor(url, base) {
|
|
28
|
+
url = new URL(url, base);
|
|
29
|
+
super(url);
|
|
30
|
+
|
|
31
|
+
current_url = this;
|
|
32
|
+
this.#searchParams = new TrackedURLSearchParams(url.searchParams);
|
|
33
|
+
current_url = null;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
get hash() {
|
|
37
|
+
return get(this.#hash);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
set hash(value) {
|
|
41
|
+
super.hash = value;
|
|
42
|
+
set(this.#hash, super.hash, this.#block);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
get host() {
|
|
46
|
+
get(this.#hostname);
|
|
47
|
+
get(this.#port);
|
|
48
|
+
return super.host;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
set host(value) {
|
|
52
|
+
super.host = value;
|
|
53
|
+
set(this.#hostname, super.hostname, this.#block);
|
|
54
|
+
set(this.#port, super.port, this.#block);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
get hostname() {
|
|
58
|
+
return get(this.#hostname);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
set hostname(value) {
|
|
62
|
+
super.hostname = value;
|
|
63
|
+
set(this.#hostname, super.hostname, this.#block);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
get href() {
|
|
67
|
+
get(this.#protocol);
|
|
68
|
+
get(this.#username);
|
|
69
|
+
get(this.#password);
|
|
70
|
+
get(this.#hostname);
|
|
71
|
+
get(this.#port);
|
|
72
|
+
get(this.#pathname);
|
|
73
|
+
get(this.#hash);
|
|
74
|
+
get(this.#search);
|
|
75
|
+
return super.href;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
set href(value) {
|
|
79
|
+
super.href = value;
|
|
80
|
+
set(this.#protocol, super.protocol, this.#block);
|
|
81
|
+
set(this.#username, super.username, this.#block);
|
|
82
|
+
set(this.#password, super.password, this.#block);
|
|
83
|
+
set(this.#hostname, super.hostname, this.#block);
|
|
84
|
+
set(this.#port, super.port, this.#block);
|
|
85
|
+
set(this.#pathname, super.pathname, this.#block);
|
|
86
|
+
set(this.#hash, super.hash, this.#block);
|
|
87
|
+
set(this.#search, super.search, this.#block);
|
|
88
|
+
this.#searchParams[REPLACE](super.searchParams);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
get password() {
|
|
92
|
+
return get(this.#password);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
set password(value) {
|
|
96
|
+
super.password = value;
|
|
97
|
+
set(this.#password, super.password, this.#block);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
get pathname() {
|
|
101
|
+
return get(this.#pathname);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
set pathname(value) {
|
|
105
|
+
super.pathname = value;
|
|
106
|
+
set(this.#pathname, super.pathname, this.#block);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
get port() {
|
|
110
|
+
return get(this.#port);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
set port(value) {
|
|
114
|
+
super.port = value;
|
|
115
|
+
set(this.#port, super.port, this.#block);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
get protocol() {
|
|
119
|
+
return get(this.#protocol);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
set protocol(value) {
|
|
123
|
+
super.protocol = value;
|
|
124
|
+
set(this.#protocol, super.protocol, this.#block);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
get search() {
|
|
128
|
+
return get(this.#search);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
set search(value) {
|
|
132
|
+
super.search = value;
|
|
133
|
+
set(this.#search, value, this.#block);
|
|
134
|
+
this.#searchParams[REPLACE](super.searchParams);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
get username() {
|
|
138
|
+
return get(this.#username);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
set username(value) {
|
|
142
|
+
super.username = value;
|
|
143
|
+
set(this.#username, super.username, this.#block);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
get origin() {
|
|
147
|
+
get(this.#protocol);
|
|
148
|
+
get(this.#hostname);
|
|
149
|
+
get(this.#port);
|
|
150
|
+
return super.origin;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
get searchParams() {
|
|
154
|
+
return this.#searchParams;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
toString() {
|
|
158
|
+
return this.href;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
toJSON() {
|
|
162
|
+
return this.href;
|
|
163
|
+
}
|
|
164
|
+
}
|
package/src/utils/builders.js
CHANGED
|
@@ -774,8 +774,39 @@ export function jsx_spread_attribute(argument) {
|
|
|
774
774
|
};
|
|
775
775
|
}
|
|
776
776
|
|
|
777
|
+
|
|
778
|
+
/**
|
|
779
|
+
* @returns {ESTree.SwitchStatement}
|
|
780
|
+
*/
|
|
781
|
+
export function switch_builder(discriminant, cases) {
|
|
782
|
+
return {
|
|
783
|
+
type: 'SwitchStatement',
|
|
784
|
+
discriminant,
|
|
785
|
+
cases,
|
|
786
|
+
};
|
|
787
|
+
}
|
|
788
|
+
|
|
789
|
+
/**
|
|
790
|
+
* @returns {ESTree.SwitchCase}
|
|
791
|
+
*/
|
|
792
|
+
export function switch_case(test = null, consequent = []) {
|
|
793
|
+
return {
|
|
794
|
+
type: 'SwitchCase',
|
|
795
|
+
test,
|
|
796
|
+
consequent,
|
|
797
|
+
};
|
|
798
|
+
}
|
|
799
|
+
|
|
777
800
|
export const void0 = unary('void', literal(0));
|
|
778
801
|
|
|
802
|
+
/**
|
|
803
|
+
* @returns {ESTree.BreakStatement}
|
|
804
|
+
*/
|
|
805
|
+
export const break_statement = {
|
|
806
|
+
type: 'BreakStatement',
|
|
807
|
+
label: null,
|
|
808
|
+
};
|
|
809
|
+
|
|
779
810
|
export {
|
|
780
811
|
await_builder as await,
|
|
781
812
|
let_builder as let,
|
|
@@ -783,7 +814,9 @@ export {
|
|
|
783
814
|
var_builder as var,
|
|
784
815
|
true_instance as true,
|
|
785
816
|
false_instance as false,
|
|
817
|
+
break_statement as break,
|
|
786
818
|
for_builder as for,
|
|
819
|
+
switch_builder as switch,
|
|
787
820
|
function_builder as function,
|
|
788
821
|
return_builder as return,
|
|
789
822
|
if_builder as if,
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
|
2
|
+
|
|
3
|
+
exports[`compiler success tests > compiles tracked values in effect with assignment expression 1`] = `"state.count = _$_.get(count);"`;
|
|
4
|
+
|
|
5
|
+
exports[`compiler success tests > compiles tracked values in effect with update expressions 1`] = `
|
|
6
|
+
"_$_.with_scope(__block, () => untrack(() => {
|
|
7
|
+
state.preIncrement = _$_.update_pre(count, __block);
|
|
8
|
+
state.postIncrement = _$_.update(count, __block);
|
|
9
|
+
state.preDecrement = _$_.update_pre(count, __block, -1);
|
|
10
|
+
state.postDecrement = _$_.update(count, __block, -1);
|
|
11
|
+
}));"
|
|
12
|
+
`;
|
|
@@ -1843,5 +1843,50 @@ describe('basic client', () => {
|
|
|
1843
1843
|
|
|
1844
1844
|
expect(error).toBe(undefined);
|
|
1845
1845
|
});
|
|
1846
|
-
});
|
|
1847
1846
|
|
|
1847
|
+
it('unwraps tracked values inside effect', () => {
|
|
1848
|
+
let state = {};
|
|
1849
|
+
|
|
1850
|
+
component Basic() {
|
|
1851
|
+
let count = track(0);
|
|
1852
|
+
|
|
1853
|
+
effect(() => {
|
|
1854
|
+
state.count = @count;
|
|
1855
|
+
})
|
|
1856
|
+
}
|
|
1857
|
+
|
|
1858
|
+
render(Basic);
|
|
1859
|
+
flushSync();
|
|
1860
|
+
|
|
1861
|
+
expect(state.count).toBe(0);
|
|
1862
|
+
});
|
|
1863
|
+
|
|
1864
|
+
it('does not unwrap values with update expressions inside effect', () => {
|
|
1865
|
+
let state = {};
|
|
1866
|
+
|
|
1867
|
+
component Basic() {
|
|
1868
|
+
let count = track(5);
|
|
1869
|
+
|
|
1870
|
+
effect(() => {
|
|
1871
|
+
untrack(() => {
|
|
1872
|
+
state.initialValue = @count;
|
|
1873
|
+
state.preIncrement = ++@count;
|
|
1874
|
+
state.postIncrement = @count++;
|
|
1875
|
+
state.preDecrement = --@count;
|
|
1876
|
+
state.postDecrement = @count--;
|
|
1877
|
+
state.finalValue = @count;
|
|
1878
|
+
});
|
|
1879
|
+
})
|
|
1880
|
+
}
|
|
1881
|
+
|
|
1882
|
+
render(Basic);
|
|
1883
|
+
flushSync();
|
|
1884
|
+
|
|
1885
|
+
expect(state.initialValue).toBe(5);
|
|
1886
|
+
expect(state.preIncrement).toBe(6);
|
|
1887
|
+
expect(state.postIncrement).toBe(6);
|
|
1888
|
+
expect(state.preDecrement).toBe(6);
|
|
1889
|
+
expect(state.postDecrement).toBe(6);
|
|
1890
|
+
expect(state.finalValue).toBe(5);
|
|
1891
|
+
});
|
|
1892
|
+
});
|
|
@@ -462,4 +462,37 @@ describe('compiler success tests', () => {
|
|
|
462
462
|
expect(splitResult.textContent).toBe('Split result: Paragraph, Content');
|
|
463
463
|
});
|
|
464
464
|
});
|
|
465
|
+
|
|
466
|
+
it('compiles tracked values in effect with assignment expression', () => {
|
|
467
|
+
const source = `component App() {
|
|
468
|
+
let count = track(0);
|
|
469
|
+
|
|
470
|
+
effect(() => {
|
|
471
|
+
state.count = @count;
|
|
472
|
+
})
|
|
473
|
+
}`;
|
|
474
|
+
const result = compile(source, 'test.ripple');
|
|
475
|
+
// Extract just the effect callback body
|
|
476
|
+
const effectMatch = result.js.code.match(/effect\(\(\) => \{([^}]+)\}\)/s);
|
|
477
|
+
expect(effectMatch[1].trim()).toMatchSnapshot();
|
|
478
|
+
});
|
|
479
|
+
|
|
480
|
+
it('compiles tracked values in effect with update expressions', () => {
|
|
481
|
+
const source = `component App() {
|
|
482
|
+
let count = track(5);
|
|
483
|
+
|
|
484
|
+
effect(() => {
|
|
485
|
+
untrack(() => {
|
|
486
|
+
state.preIncrement = ++@count;
|
|
487
|
+
state.postIncrement = @count++;
|
|
488
|
+
state.preDecrement = --@count;
|
|
489
|
+
state.postDecrement = @count--;
|
|
490
|
+
});
|
|
491
|
+
})
|
|
492
|
+
}`;
|
|
493
|
+
const result = compile(source, 'test.ripple');
|
|
494
|
+
// Extract just the effect callback body
|
|
495
|
+
const effectMatch = result.js.code.match(/effect\(\(\) => \{([\s\S]+?)\n\t\}\)\)/);
|
|
496
|
+
expect(effectMatch[1].trim()).toMatchSnapshot();
|
|
497
|
+
});
|
|
465
498
|
});
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
|
|
2
|
+
import { mount, flushSync, MediaQuery, track } from 'ripple';
|
|
3
|
+
|
|
4
|
+
function setupMatchMedia() {
|
|
5
|
+
let listeners = new Set();
|
|
6
|
+
|
|
7
|
+
// A mock implementation of matchMedia
|
|
8
|
+
const mockMatchMedia = vi.fn().mockImplementation(query => {
|
|
9
|
+
return {
|
|
10
|
+
media: query,
|
|
11
|
+
matches: false, // default value
|
|
12
|
+
addEventListener: (type, cb) => {
|
|
13
|
+
if (type === 'change') listeners.add(cb);
|
|
14
|
+
},
|
|
15
|
+
removeEventListener: (type, cb) => {
|
|
16
|
+
if (type === 'change') listeners.delete(cb);
|
|
17
|
+
},
|
|
18
|
+
/** @param {function(MediaQueryListEvent): void} cb */
|
|
19
|
+
addListener: (cb) => listeners.add(cb),
|
|
20
|
+
/** @param {function(MediaQueryListEvent): void} cb */
|
|
21
|
+
removeListener: (cb) => listeners.delete(cb),
|
|
22
|
+
dispatch: (event) => {
|
|
23
|
+
listeners.forEach((cb) => cb(event));
|
|
24
|
+
},
|
|
25
|
+
listenersCount: () => listeners.size,
|
|
26
|
+
};
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
Object.defineProperty(window, 'matchMedia', {
|
|
30
|
+
writable: true,
|
|
31
|
+
value: mockMatchMedia
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
return { mockMatchMedia, listeners };
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
describe('MediaQuery', () => {
|
|
38
|
+
let container;
|
|
39
|
+
let mm;
|
|
40
|
+
|
|
41
|
+
function render(component) {
|
|
42
|
+
mount(component, {
|
|
43
|
+
target: container
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
beforeEach(() => {
|
|
48
|
+
mm = setupMatchMedia();
|
|
49
|
+
container = document.createElement('div');
|
|
50
|
+
document.body.appendChild(container);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
afterEach(() => {
|
|
54
|
+
document.body.removeChild(container);
|
|
55
|
+
container = null;
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it('should be reactive if matchMedia changes', () => {
|
|
59
|
+
const media = '(min-width: 600px)';
|
|
60
|
+
|
|
61
|
+
component App() {
|
|
62
|
+
const medium = new MediaQuery(media);
|
|
63
|
+
|
|
64
|
+
<div>
|
|
65
|
+
<p>{@medium}</p>
|
|
66
|
+
</div>
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
render(App);
|
|
70
|
+
flushSync();
|
|
71
|
+
|
|
72
|
+
const event = new Event('change');
|
|
73
|
+
Object.assign(event, { matches: true, media: media });
|
|
74
|
+
|
|
75
|
+
const p = container.querySelector('p');
|
|
76
|
+
expect(p.textContent).toBe('false');
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
mm.mockMatchMedia.mock.results[0].value.matches = true;
|
|
80
|
+
mm.mockMatchMedia.mock.results[0].value.dispatch(event);
|
|
81
|
+
flushSync();
|
|
82
|
+
|
|
83
|
+
expect(p.textContent).toBe('true');
|
|
84
|
+
|
|
85
|
+
Object.assign(event, { matches: false, media: media });
|
|
86
|
+
|
|
87
|
+
mm.mockMatchMedia.mock.results[0].value.matches = false;
|
|
88
|
+
mm.mockMatchMedia.mock.results[0].value.dispatch(event);
|
|
89
|
+
flushSync();
|
|
90
|
+
|
|
91
|
+
expect(p.textContent).toBe('false');
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
it('should have cleared event listeners after unmount', async () => {
|
|
96
|
+
const media = '(min-width: 600px)';
|
|
97
|
+
|
|
98
|
+
component App() {
|
|
99
|
+
let show = track(true);
|
|
100
|
+
|
|
101
|
+
if (@show) {
|
|
102
|
+
<Child />
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
<button onClick={() => @show = !@show}>{'Toggle Child'}</button>
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
component Child() {
|
|
109
|
+
const medium = new MediaQuery(media);
|
|
110
|
+
|
|
111
|
+
<div>
|
|
112
|
+
<p>{@medium}</p>
|
|
113
|
+
</div>
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
render(App);
|
|
117
|
+
flushSync();
|
|
118
|
+
|
|
119
|
+
expect(mm.mockMatchMedia.mock.results[0].value.listenersCount()).toBe(1);
|
|
120
|
+
|
|
121
|
+
const button = container.querySelector('button');
|
|
122
|
+
button.click();
|
|
123
|
+
flushSync();
|
|
124
|
+
|
|
125
|
+
// wait for microtask queue to flush
|
|
126
|
+
await new Promise(resolve => setTimeout(resolve, 0));
|
|
127
|
+
|
|
128
|
+
expect(mm.mockMatchMedia.mock.results[0].value.listenersCount()).toBe(0);
|
|
129
|
+
});
|
|
130
|
+
});
|