Skip to content

Commit 0a9ba5a

Browse files
committed
trackedSet
1 parent e40cce4 commit 0a9ba5a

File tree

6 files changed

+638
-585
lines changed

6 files changed

+638
-585
lines changed

packages/@glimmer-workspace/integration-tests/lib/render-test.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -546,7 +546,10 @@ export class RenderTest implements IRenderTest {
546546
}
547547

548548
protected assertEachReactivity(
549-
Klass: new (...args: any[]) => { collection: number[]; update: () => void }
549+
Klass: new (...args: any[]) => {
550+
collection: (string | number)[] | Set<number | string>;
551+
update: () => void;
552+
}
550553
) {
551554
let instance: TestComponent | undefined;
552555

Lines changed: 270 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,270 @@
1+
import { trackedSet } from '@glimmer/validator';
2+
import {
3+
GlimmerishComponent as Component,
4+
jitSuite,
5+
RenderTest,
6+
test,
7+
} from '@glimmer-workspace/integration-tests';
8+
9+
class TrackedSetTest extends RenderTest {
10+
static suiteName = `trackedSet() (rendering)`;
11+
12+
@test
13+
'add/has'() {
14+
this.assertReactivity(
15+
class extends Component {
16+
set = trackedSet();
17+
18+
get value() {
19+
return this.set.has('foo');
20+
}
21+
22+
update() {
23+
this.set.add('foo');
24+
}
25+
}
26+
);
27+
}
28+
29+
@test
30+
'add/has existing value'() {
31+
this.assertReactivity(
32+
class extends Component {
33+
set = trackedSet(['foo']);
34+
35+
get value() {
36+
return this.set.has('foo');
37+
}
38+
39+
update() {
40+
this.set.add('foo');
41+
}
42+
},
43+
false
44+
);
45+
}
46+
47+
@test
48+
'add/has existing value (with always-dirty)'() {
49+
this.assertReactivity(
50+
class extends Component {
51+
set = trackedSet(['foo'], { equals: () => false });
52+
53+
get value() {
54+
return this.set.has('foo');
55+
}
56+
57+
update() {
58+
this.set.add('foo');
59+
}
60+
}
61+
);
62+
}
63+
64+
@test
65+
'add/has unrelated value'() {
66+
this.assertReactivity(
67+
class extends Component {
68+
set = trackedSet();
69+
70+
get value() {
71+
return this.set.has('foo');
72+
}
73+
74+
update() {
75+
this.set.add('bar');
76+
}
77+
},
78+
false
79+
);
80+
}
81+
82+
@test
83+
entries() {
84+
this.assertReactivity(
85+
class extends Component {
86+
set = trackedSet();
87+
88+
get value() {
89+
return this.set.entries();
90+
}
91+
92+
update() {
93+
this.set.add('foo');
94+
}
95+
}
96+
);
97+
}
98+
99+
@test
100+
keys() {
101+
this.assertReactivity(
102+
class extends Component {
103+
set = trackedSet();
104+
105+
get value() {
106+
return this.set.keys();
107+
}
108+
109+
update() {
110+
this.set.add('foo');
111+
}
112+
}
113+
);
114+
}
115+
116+
@test
117+
values() {
118+
this.assertReactivity(
119+
class extends Component {
120+
set = trackedSet();
121+
122+
get value() {
123+
return this.set.values();
124+
}
125+
126+
update() {
127+
this.set.add('foo');
128+
}
129+
}
130+
);
131+
}
132+
133+
@test
134+
forEach() {
135+
this.assertReactivity(
136+
class extends Component {
137+
set = trackedSet();
138+
139+
get value() {
140+
this.set.forEach(() => {
141+
/* no-op */
142+
});
143+
return 'test';
144+
}
145+
146+
update() {
147+
this.set.add('foo');
148+
}
149+
}
150+
);
151+
}
152+
153+
@test
154+
size() {
155+
this.assertReactivity(
156+
class extends Component {
157+
set = trackedSet();
158+
159+
get value() {
160+
return this.set.size;
161+
}
162+
163+
update() {
164+
this.set.add('foo');
165+
}
166+
}
167+
);
168+
}
169+
170+
@test
171+
delete() {
172+
this.assertReactivity(
173+
class extends Component {
174+
set = trackedSet(['foo', 123]);
175+
176+
get value() {
177+
return this.set.has('foo');
178+
}
179+
180+
update() {
181+
this.set.delete('foo');
182+
}
183+
}
184+
);
185+
}
186+
187+
@test
188+
'delete unrelated value'() {
189+
this.assertReactivity(
190+
class extends Component {
191+
set = trackedSet(['foo', 123]);
192+
193+
get value() {
194+
return this.set.has('foo');
195+
}
196+
197+
update() {
198+
this.set.delete(123);
199+
}
200+
},
201+
false
202+
);
203+
}
204+
205+
@test
206+
clear() {
207+
this.assertReactivity(
208+
class extends Component {
209+
set = trackedSet(['foo', 123]);
210+
211+
get value() {
212+
return this.set.has('foo');
213+
}
214+
215+
update() {
216+
this.set.clear();
217+
}
218+
}
219+
);
220+
}
221+
222+
@test
223+
'each: add'() {
224+
this.assertEachReactivity(
225+
class extends Component {
226+
collection = trackedSet(['foo', 123]);
227+
228+
update() {
229+
this.collection.add('bar');
230+
}
231+
}
232+
);
233+
}
234+
235+
@test
236+
'each: add existing value'() {
237+
this.assertEachReactivity(
238+
class extends Component {
239+
collection = trackedSet(['foo', 123]);
240+
241+
update() {
242+
this.collection.add('foo');
243+
}
244+
}
245+
);
246+
}
247+
}
248+
249+
jitSuite(TrackedSetTest);
250+
251+
// TODO: These tests are currently unstable on release, turn back on once
252+
// behavior is fixed
253+
// eachInReactivityTest(
254+
// 'add',
255+
// class extends Component {
256+
// collection = trackedSet(['foo', 123]);
257+
// update() {
258+
// this.collection.add('bar');
259+
// }
260+
// }
261+
// );
262+
// eachInReactivityTest(
263+
// 'add existing value',
264+
// class extends Component {
265+
// collection = trackedSet(['foo', 123]);
266+
// update() {
267+
// this.collection.add('foo');
268+
// }
269+
// }
270+
// );

packages/@glimmer/validator/lib/collections/set.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import type { ReactiveOptions } from './types';
33
import { consumeTag } from '../tracking';
44
import { createUpdatableTag, DIRTY_TAG } from '../validators';
55

6-
class TrackedSet<T extends WeakKey> implements Set<T> {
6+
class TrackedSet<T = unknown> implements Set<T> {
77
#options: ReactiveOptions<T>;
88
#collection = createUpdatableTag();
99
#storages = new Map<T, ReturnType<typeof createUpdatableTag>>();
@@ -29,7 +29,7 @@ class TrackedSet<T extends WeakKey> implements Set<T> {
2929
}
3030
}
3131

32-
constructor(existing?: readonly T[] | Iterable<T> | null, options: ReactiveOptions<T>) {
32+
constructor(existing: Iterable<T>, options: ReactiveOptions<T>) {
3333
this.#vals = new Set(existing);
3434
this.#options = options;
3535
}
@@ -205,7 +205,7 @@ class TrackedSet<T extends WeakKey> implements Set<T> {
205205
Object.setPrototypeOf(TrackedSet.prototype, Set.prototype);
206206

207207
export function trackedSet<Value = unknown>(
208-
data?: Set<Value> | Iterable<Value> | null,
208+
data?: Set<Value> | Value[] | Iterable<Value> | null,
209209
options?: { equals?: (a: Value, b: Value) => boolean; description?: string }
210210
): Set<Value> {
211211
return new TrackedSet(data ?? [], {

0 commit comments

Comments
 (0)