jazz-tools 0.19.0 → 0.19.2
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/.turbo/turbo-build.log +53 -53
- package/CHANGELOG.md +21 -0
- package/dist/{chunk-P3YLNFN4.js → chunk-NCNM6UDZ.js} +61 -22
- package/dist/chunk-NCNM6UDZ.js.map +1 -0
- package/dist/index.js +1 -1
- package/dist/inspector/{custom-element-QESCMFY7.js → custom-element-ABVPHX53.js} +1118 -465
- package/dist/inspector/custom-element-ABVPHX53.js.map +1 -0
- package/dist/inspector/index.js +1090 -437
- package/dist/inspector/index.js.map +1 -1
- package/dist/inspector/register-custom-element.js +1 -1
- package/dist/inspector/tests/utils/history.test.d.ts +2 -0
- package/dist/inspector/tests/utils/history.test.d.ts.map +1 -0
- package/dist/inspector/tests/viewer/co-value-editor.test.d.ts +2 -0
- package/dist/inspector/tests/viewer/co-value-editor.test.d.ts.map +1 -0
- package/dist/inspector/tests/viewer/comap-view.test.d.ts +2 -0
- package/dist/inspector/tests/viewer/comap-view.test.d.ts.map +1 -0
- package/dist/inspector/ui/icon.d.ts +6 -0
- package/dist/inspector/ui/icon.d.ts.map +1 -1
- package/dist/inspector/ui/icons/add-icon.d.ts +2 -0
- package/dist/inspector/ui/icons/add-icon.d.ts.map +1 -0
- package/dist/inspector/ui/icons/edit-icon.d.ts +2 -0
- package/dist/inspector/ui/icons/edit-icon.d.ts.map +1 -0
- package/dist/inspector/ui/icons/history.d.ts +2 -0
- package/dist/inspector/ui/icons/history.d.ts.map +1 -0
- package/dist/inspector/utils/history.d.ts +3 -0
- package/dist/inspector/utils/history.d.ts.map +1 -0
- package/dist/inspector/utils/transactions-changes.d.ts +38 -0
- package/dist/inspector/utils/transactions-changes.d.ts.map +1 -0
- package/dist/inspector/viewer/co-map-view.d.ts +9 -0
- package/dist/inspector/viewer/co-map-view.d.ts.map +1 -0
- package/dist/inspector/viewer/co-value-editor.d.ts +10 -0
- package/dist/inspector/viewer/co-value-editor.d.ts.map +1 -0
- package/dist/inspector/viewer/grid-view.d.ts +3 -2
- package/dist/inspector/viewer/grid-view.d.ts.map +1 -1
- package/dist/inspector/viewer/history-view.d.ts.map +1 -1
- package/dist/inspector/viewer/page.d.ts.map +1 -1
- package/dist/testing.js +1 -1
- package/dist/tools/coValues/CoFieldInit.d.ts +2 -1
- package/dist/tools/coValues/CoFieldInit.d.ts.map +1 -1
- package/dist/tools/coValues/deepLoading.d.ts +8 -7
- package/dist/tools/coValues/deepLoading.d.ts.map +1 -1
- package/dist/tools/coValues/interfaces.d.ts +3 -3
- package/dist/tools/coValues/interfaces.d.ts.map +1 -1
- package/dist/tools/coValues/schemaUnion.d.ts +6 -9
- package/dist/tools/coValues/schemaUnion.d.ts.map +1 -1
- package/dist/tools/implementation/zodSchema/schemaTypes/CoDiscriminatedUnionSchema.d.ts +18 -7
- package/dist/tools/implementation/zodSchema/schemaTypes/CoDiscriminatedUnionSchema.d.ts.map +1 -1
- package/dist/tools/implementation/zodSchema/typeConverters/CoFieldSchemaInit.d.ts +3 -2
- package/dist/tools/implementation/zodSchema/typeConverters/CoFieldSchemaInit.d.ts.map +1 -1
- package/dist/tools/implementation/zodSchema/unionUtils.d.ts.map +1 -1
- package/dist/tools/subscribe/SubscriptionScope.d.ts +1 -0
- package/dist/tools/subscribe/SubscriptionScope.d.ts.map +1 -1
- package/package.json +4 -4
- package/src/inspector/tests/utils/history.test.ts +401 -0
- package/src/inspector/tests/viewer/co-value-editor.test.tsx +903 -0
- package/src/inspector/tests/viewer/comap-view.test.tsx +581 -0
- package/src/inspector/ui/icon.tsx +6 -0
- package/src/inspector/ui/icons/add-icon.tsx +21 -0
- package/src/inspector/ui/icons/edit-icon.tsx +17 -0
- package/src/inspector/ui/icons/history.tsx +28 -0
- package/src/inspector/ui/modal.tsx +3 -3
- package/src/inspector/utils/history.ts +49 -0
- package/src/inspector/utils/transactions-changes.ts +98 -0
- package/src/inspector/viewer/co-map-view.tsx +312 -0
- package/src/inspector/viewer/co-value-editor.tsx +164 -0
- package/src/inspector/viewer/grid-view.tsx +139 -10
- package/src/inspector/viewer/history-view.tsx +16 -118
- package/src/inspector/viewer/page.tsx +13 -0
- package/src/react-core/tests/usePassPhraseAuth.test.ts +1 -1
- package/src/tools/coValues/CoFieldInit.ts +6 -3
- package/src/tools/coValues/coList.ts +1 -1
- package/src/tools/coValues/deepLoading.ts +85 -71
- package/src/tools/coValues/interfaces.ts +3 -3
- package/src/tools/coValues/schemaUnion.ts +19 -14
- package/src/tools/implementation/zodSchema/schemaTypes/CoDiscriminatedUnionSchema.ts +69 -9
- package/src/tools/implementation/zodSchema/typeConverters/CoFieldSchemaInit.ts +12 -7
- package/src/tools/implementation/zodSchema/unionUtils.ts +35 -4
- package/src/tools/subscribe/SubscriptionScope.ts +3 -14
- package/src/tools/tests/coDiscriminatedUnion.test.ts +347 -5
- package/src/tools/tests/coVector.test.ts +43 -0
- package/src/tools/tests/deepLoading.test.ts +55 -59
- package/src/tools/tests/schema.resolved.test.ts +70 -1
- package/dist/chunk-P3YLNFN4.js.map +0 -1
- package/dist/inspector/custom-element-QESCMFY7.js.map +0 -1
|
@@ -0,0 +1,581 @@
|
|
|
1
|
+
// @vitest-environment happy-dom
|
|
2
|
+
import { afterEach, beforeAll, describe, expect, it } from "vitest";
|
|
3
|
+
import { createJazzTestAccount, setupJazzTestSync } from "jazz-tools/testing";
|
|
4
|
+
import { co, z } from "jazz-tools";
|
|
5
|
+
import {
|
|
6
|
+
cleanup,
|
|
7
|
+
fireEvent,
|
|
8
|
+
render,
|
|
9
|
+
screen,
|
|
10
|
+
waitFor,
|
|
11
|
+
} from "@testing-library/react";
|
|
12
|
+
import { CoMapView } from "../../viewer/co-map-view";
|
|
13
|
+
import { setup } from "goober";
|
|
14
|
+
import React from "react";
|
|
15
|
+
import { JsonObject } from "cojson";
|
|
16
|
+
|
|
17
|
+
const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
18
|
+
|
|
19
|
+
describe("CoMapView", async () => {
|
|
20
|
+
const account = await setupJazzTestSync();
|
|
21
|
+
|
|
22
|
+
beforeAll(() => {
|
|
23
|
+
setup(React.createElement);
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
afterEach(() => {
|
|
27
|
+
cleanup();
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
describe("Basic Rendering", () => {
|
|
31
|
+
it("should render GridView with data", async () => {
|
|
32
|
+
const value = co
|
|
33
|
+
.map({
|
|
34
|
+
pet: z.string(),
|
|
35
|
+
age: z.number(),
|
|
36
|
+
})
|
|
37
|
+
.create({ pet: "dog", age: 10 }, account);
|
|
38
|
+
|
|
39
|
+
const data = value.$jazz.raw.toJSON() as JsonObject;
|
|
40
|
+
|
|
41
|
+
render(
|
|
42
|
+
<CoMapView
|
|
43
|
+
coValue={value.$jazz.raw}
|
|
44
|
+
data={data}
|
|
45
|
+
node={account.$jazz.localNode}
|
|
46
|
+
onNavigate={() => {}}
|
|
47
|
+
/>,
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
expect(screen.getByText("pet")).toBeDefined();
|
|
51
|
+
expect(screen.getByText("age")).toBeDefined();
|
|
52
|
+
expect(screen.getByText("dog")).toBeDefined();
|
|
53
|
+
expect(screen.getByText("10")).toBeDefined();
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it("should render restore button", async () => {
|
|
57
|
+
const value = co
|
|
58
|
+
.map({
|
|
59
|
+
foo: z.string(),
|
|
60
|
+
})
|
|
61
|
+
.create({ foo: "bar" }, account);
|
|
62
|
+
|
|
63
|
+
const data = value.$jazz.raw.toJSON() as JsonObject;
|
|
64
|
+
|
|
65
|
+
render(
|
|
66
|
+
<CoMapView
|
|
67
|
+
coValue={value.$jazz.raw}
|
|
68
|
+
data={data}
|
|
69
|
+
node={account.$jazz.localNode}
|
|
70
|
+
onNavigate={() => {}}
|
|
71
|
+
/>,
|
|
72
|
+
);
|
|
73
|
+
|
|
74
|
+
const restoreButton = screen.getByTitle("Timeline");
|
|
75
|
+
expect(restoreButton).toBeDefined();
|
|
76
|
+
});
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
describe("Timestamp Selection", () => {
|
|
80
|
+
it("should display timestamps and allow selection", async () => {
|
|
81
|
+
const value = co
|
|
82
|
+
.map({
|
|
83
|
+
pet: z.string(),
|
|
84
|
+
})
|
|
85
|
+
.create({ pet: "dog" }, account);
|
|
86
|
+
|
|
87
|
+
await sleep(2);
|
|
88
|
+
value.$jazz.set("pet", "cat");
|
|
89
|
+
await sleep(2);
|
|
90
|
+
value.$jazz.set("pet", "bird");
|
|
91
|
+
|
|
92
|
+
const data = value.$jazz.raw.toJSON() as JsonObject;
|
|
93
|
+
|
|
94
|
+
render(
|
|
95
|
+
<CoMapView
|
|
96
|
+
coValue={value.$jazz.raw}
|
|
97
|
+
data={data}
|
|
98
|
+
node={account.$jazz.localNode}
|
|
99
|
+
onNavigate={() => {}}
|
|
100
|
+
/>,
|
|
101
|
+
);
|
|
102
|
+
|
|
103
|
+
const restoreButton = screen.getByTitle("Timeline");
|
|
104
|
+
fireEvent.click(restoreButton);
|
|
105
|
+
|
|
106
|
+
const slider = screen.getByRole("slider") as HTMLInputElement;
|
|
107
|
+
expect(slider).toBeDefined();
|
|
108
|
+
expect(slider.max).toBe("2");
|
|
109
|
+
|
|
110
|
+
fireEvent.change(slider, { target: { value: "0" } });
|
|
111
|
+
expect(slider.value).toBe("0");
|
|
112
|
+
|
|
113
|
+
fireEvent.change(slider, { target: { value: "1" } });
|
|
114
|
+
expect(slider.value).toBe("1");
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
it("should show timestamp in ISO format", async () => {
|
|
118
|
+
const value = co
|
|
119
|
+
.map({
|
|
120
|
+
foo: z.string(),
|
|
121
|
+
})
|
|
122
|
+
.create({ foo: "bar" }, account);
|
|
123
|
+
|
|
124
|
+
value.$jazz.set("foo", "baz");
|
|
125
|
+
|
|
126
|
+
const data = value.$jazz.raw.toJSON() as JsonObject;
|
|
127
|
+
|
|
128
|
+
render(
|
|
129
|
+
<CoMapView
|
|
130
|
+
coValue={value.$jazz.raw}
|
|
131
|
+
data={data}
|
|
132
|
+
node={account.$jazz.localNode}
|
|
133
|
+
onNavigate={() => {}}
|
|
134
|
+
/>,
|
|
135
|
+
);
|
|
136
|
+
|
|
137
|
+
const restoreButton = screen.getByTitle("Timeline");
|
|
138
|
+
fireEvent.click(restoreButton);
|
|
139
|
+
|
|
140
|
+
const timestampDisplay = screen.getAllByText(/\d{4}-\d{2}-\d{2}T/)[0];
|
|
141
|
+
expect(timestampDisplay).toBeDefined();
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
it("should update preview when timestamp changes", async () => {
|
|
145
|
+
const value = co
|
|
146
|
+
.map({
|
|
147
|
+
pet: z.string(),
|
|
148
|
+
})
|
|
149
|
+
.create({ pet: "dog" }, account);
|
|
150
|
+
|
|
151
|
+
// wait to have different timestamps in transactions
|
|
152
|
+
await sleep(2);
|
|
153
|
+
value.$jazz.set("pet", "cat");
|
|
154
|
+
await sleep(2);
|
|
155
|
+
value.$jazz.set("pet", "bird");
|
|
156
|
+
|
|
157
|
+
const data = value.$jazz.raw.toJSON() as JsonObject;
|
|
158
|
+
|
|
159
|
+
render(
|
|
160
|
+
<CoMapView
|
|
161
|
+
coValue={value.$jazz.raw}
|
|
162
|
+
data={data}
|
|
163
|
+
node={account.$jazz.localNode}
|
|
164
|
+
onNavigate={() => {}}
|
|
165
|
+
/>,
|
|
166
|
+
);
|
|
167
|
+
|
|
168
|
+
const restoreButton = screen.getByTitle("Timeline");
|
|
169
|
+
fireEvent.click(restoreButton);
|
|
170
|
+
|
|
171
|
+
const slider = screen.getByRole("slider") as HTMLInputElement;
|
|
172
|
+
const preview = screen.getByText(/State at that time:/);
|
|
173
|
+
expect(preview).toBeDefined();
|
|
174
|
+
|
|
175
|
+
// Modal starts at the most recent timestamp (last index)
|
|
176
|
+
await waitFor(() => {
|
|
177
|
+
const previewPre = preview.parentElement?.querySelector("pre");
|
|
178
|
+
expect(previewPre?.textContent).toContain("bird");
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
fireEvent.change(slider, { target: { value: 0 } });
|
|
182
|
+
await waitFor(() => {
|
|
183
|
+
const previewPre = preview.parentElement?.querySelector("pre");
|
|
184
|
+
expect(previewPre?.textContent).toContain("dog");
|
|
185
|
+
});
|
|
186
|
+
});
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
describe("Remove Unknown Properties Checkbox", () => {
|
|
190
|
+
it("should toggle checkbox state", async () => {
|
|
191
|
+
const value = co
|
|
192
|
+
.map({
|
|
193
|
+
foo: z.string(),
|
|
194
|
+
})
|
|
195
|
+
.create({ foo: "bar" }, account);
|
|
196
|
+
|
|
197
|
+
value.$jazz.set("foo", "baz");
|
|
198
|
+
|
|
199
|
+
const data = value.$jazz.raw.toJSON() as JsonObject;
|
|
200
|
+
|
|
201
|
+
render(
|
|
202
|
+
<CoMapView
|
|
203
|
+
coValue={value.$jazz.raw}
|
|
204
|
+
data={data}
|
|
205
|
+
node={account.$jazz.localNode}
|
|
206
|
+
onNavigate={() => {}}
|
|
207
|
+
/>,
|
|
208
|
+
);
|
|
209
|
+
|
|
210
|
+
const restoreButton = screen.getByTitle("Timeline");
|
|
211
|
+
fireEvent.click(restoreButton);
|
|
212
|
+
|
|
213
|
+
const checkbox = screen.getByRole("checkbox") as HTMLInputElement;
|
|
214
|
+
expect(checkbox.checked).toBe(false);
|
|
215
|
+
|
|
216
|
+
fireEvent.click(checkbox);
|
|
217
|
+
expect(checkbox.checked).toBe(true);
|
|
218
|
+
|
|
219
|
+
fireEvent.click(checkbox);
|
|
220
|
+
expect(checkbox.checked).toBe(false);
|
|
221
|
+
});
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
describe("Restore Functionality", () => {
|
|
225
|
+
it("should show preview of state to restore", async () => {
|
|
226
|
+
const value = co
|
|
227
|
+
.map({
|
|
228
|
+
pet: z.string(),
|
|
229
|
+
age: z.number(),
|
|
230
|
+
})
|
|
231
|
+
.create({ pet: "dog", age: 10 }, account);
|
|
232
|
+
|
|
233
|
+
await sleep(2);
|
|
234
|
+
value.$jazz.set("pet", "cat");
|
|
235
|
+
value.$jazz.set("age", 20);
|
|
236
|
+
|
|
237
|
+
const data = value.$jazz.raw.toJSON() as JsonObject;
|
|
238
|
+
|
|
239
|
+
render(
|
|
240
|
+
<CoMapView
|
|
241
|
+
coValue={value.$jazz.raw}
|
|
242
|
+
data={data}
|
|
243
|
+
node={account.$jazz.localNode}
|
|
244
|
+
onNavigate={() => {}}
|
|
245
|
+
/>,
|
|
246
|
+
);
|
|
247
|
+
|
|
248
|
+
const restoreButton = screen.getByTitle("Timeline");
|
|
249
|
+
fireEvent.click(restoreButton);
|
|
250
|
+
|
|
251
|
+
const slider = screen.getByRole("slider") as HTMLInputElement;
|
|
252
|
+
fireEvent.change(slider, { target: { value: 0 } });
|
|
253
|
+
|
|
254
|
+
await waitFor(() => {
|
|
255
|
+
const preview = screen.getByText(/State at that time:/);
|
|
256
|
+
const previewPre = preview.parentElement?.querySelector("pre");
|
|
257
|
+
expect(previewPre?.textContent).toContain("dog");
|
|
258
|
+
expect(previewPre?.textContent).toContain("10");
|
|
259
|
+
});
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
it("should close modal when restore is clicked", async () => {
|
|
263
|
+
const value = co
|
|
264
|
+
.map({
|
|
265
|
+
pet: z.string(),
|
|
266
|
+
age: z.number(),
|
|
267
|
+
})
|
|
268
|
+
.create({ pet: "dog", age: 10 }, account);
|
|
269
|
+
|
|
270
|
+
await sleep(2);
|
|
271
|
+
value.$jazz.set("pet", "cat");
|
|
272
|
+
|
|
273
|
+
const data = value.$jazz.raw.toJSON() as JsonObject;
|
|
274
|
+
|
|
275
|
+
render(
|
|
276
|
+
<CoMapView
|
|
277
|
+
coValue={value.$jazz.raw}
|
|
278
|
+
data={data}
|
|
279
|
+
node={account.$jazz.localNode}
|
|
280
|
+
onNavigate={() => {}}
|
|
281
|
+
/>,
|
|
282
|
+
);
|
|
283
|
+
|
|
284
|
+
const restoreButton = screen.getByTitle("Timeline");
|
|
285
|
+
fireEvent.click(restoreButton);
|
|
286
|
+
|
|
287
|
+
expect(screen.getByText("Select Timestamp")).toBeDefined();
|
|
288
|
+
|
|
289
|
+
const slider = screen.getByRole("slider") as HTMLInputElement;
|
|
290
|
+
fireEvent.change(slider, { target: { value: 0 } });
|
|
291
|
+
|
|
292
|
+
const restoreActionButton = screen.getByText("Restore");
|
|
293
|
+
fireEvent.click(restoreActionButton);
|
|
294
|
+
|
|
295
|
+
await waitFor(() => {
|
|
296
|
+
expect(screen.queryByText("Select Timestamp")).toBeNull();
|
|
297
|
+
});
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
it("should allow selecting timestamp and checking remove properties option", async () => {
|
|
301
|
+
const value = co
|
|
302
|
+
.map({
|
|
303
|
+
pet: z.string(),
|
|
304
|
+
age: z.number().optional(),
|
|
305
|
+
})
|
|
306
|
+
.create({ pet: "dog" }, account);
|
|
307
|
+
|
|
308
|
+
await sleep(2);
|
|
309
|
+
value.$jazz.set("age", 10);
|
|
310
|
+
await sleep(2);
|
|
311
|
+
value.$jazz.set("pet", "cat");
|
|
312
|
+
|
|
313
|
+
const data = value.$jazz.raw.toJSON() as JsonObject;
|
|
314
|
+
|
|
315
|
+
render(
|
|
316
|
+
<CoMapView
|
|
317
|
+
coValue={value.$jazz.raw}
|
|
318
|
+
data={data}
|
|
319
|
+
node={account.$jazz.localNode}
|
|
320
|
+
onNavigate={() => {}}
|
|
321
|
+
/>,
|
|
322
|
+
);
|
|
323
|
+
|
|
324
|
+
const restoreButton = screen.getByTitle("Timeline");
|
|
325
|
+
fireEvent.click(restoreButton);
|
|
326
|
+
|
|
327
|
+
const checkbox = screen.getByRole("checkbox") as HTMLInputElement;
|
|
328
|
+
expect(checkbox.checked).toBe(false);
|
|
329
|
+
|
|
330
|
+
fireEvent.click(checkbox);
|
|
331
|
+
expect(checkbox.checked).toBe(true);
|
|
332
|
+
|
|
333
|
+
// Change to earlier timestamp
|
|
334
|
+
const slider = screen.getByRole("slider") as HTMLInputElement;
|
|
335
|
+
fireEvent.change(slider, { target: { value: "0" } });
|
|
336
|
+
|
|
337
|
+
await waitFor(() => {
|
|
338
|
+
const preview = screen.getByText(/State at that time:/);
|
|
339
|
+
const previewPre = preview.parentElement?.querySelector("pre");
|
|
340
|
+
expect(previewPre?.textContent).toContain("dog");
|
|
341
|
+
});
|
|
342
|
+
|
|
343
|
+
const restoreActionButton = screen.getByText(
|
|
344
|
+
"Restore",
|
|
345
|
+
) as HTMLButtonElement;
|
|
346
|
+
expect(restoreActionButton.disabled).toBe(false);
|
|
347
|
+
});
|
|
348
|
+
});
|
|
349
|
+
|
|
350
|
+
describe("Edge Cases", () => {
|
|
351
|
+
it("should handle empty CoMap", async () => {
|
|
352
|
+
const value = co.map({}).create({}, account);
|
|
353
|
+
|
|
354
|
+
const data = value.$jazz.raw.toJSON() as JsonObject;
|
|
355
|
+
|
|
356
|
+
render(
|
|
357
|
+
<CoMapView
|
|
358
|
+
coValue={value.$jazz.raw}
|
|
359
|
+
data={data}
|
|
360
|
+
node={account.$jazz.localNode}
|
|
361
|
+
onNavigate={() => {}}
|
|
362
|
+
/>,
|
|
363
|
+
);
|
|
364
|
+
|
|
365
|
+
expect(screen.getByTitle("Timeline")).toBeDefined();
|
|
366
|
+
});
|
|
367
|
+
|
|
368
|
+
it("should handle complex data types", async () => {
|
|
369
|
+
const value = co
|
|
370
|
+
.map({
|
|
371
|
+
obj: z.object({
|
|
372
|
+
name: z.string(),
|
|
373
|
+
count: z.number(),
|
|
374
|
+
}),
|
|
375
|
+
date: z.date(),
|
|
376
|
+
bool: z.boolean(),
|
|
377
|
+
})
|
|
378
|
+
.create(
|
|
379
|
+
{
|
|
380
|
+
obj: { name: "test", count: 42 },
|
|
381
|
+
date: new Date("2024-01-01"),
|
|
382
|
+
bool: true,
|
|
383
|
+
},
|
|
384
|
+
account,
|
|
385
|
+
);
|
|
386
|
+
|
|
387
|
+
const data = value.$jazz.raw.toJSON() as JsonObject;
|
|
388
|
+
|
|
389
|
+
render(
|
|
390
|
+
<CoMapView
|
|
391
|
+
coValue={value.$jazz.raw}
|
|
392
|
+
data={data}
|
|
393
|
+
node={account.$jazz.localNode}
|
|
394
|
+
onNavigate={() => {}}
|
|
395
|
+
/>,
|
|
396
|
+
);
|
|
397
|
+
|
|
398
|
+
expect(screen.getByText("obj")).toBeDefined();
|
|
399
|
+
expect(screen.getByText("date")).toBeDefined();
|
|
400
|
+
expect(screen.getByText("bool")).toBeDefined();
|
|
401
|
+
});
|
|
402
|
+
|
|
403
|
+
it("should not allow restoring to same state (no changes)", async () => {
|
|
404
|
+
const value = co
|
|
405
|
+
.map({
|
|
406
|
+
pet: z.string(),
|
|
407
|
+
})
|
|
408
|
+
.create({ pet: "dog" }, account);
|
|
409
|
+
|
|
410
|
+
const data = value.$jazz.raw.toJSON() as JsonObject;
|
|
411
|
+
|
|
412
|
+
render(
|
|
413
|
+
<CoMapView
|
|
414
|
+
coValue={value.$jazz.raw}
|
|
415
|
+
data={data}
|
|
416
|
+
node={account.$jazz.localNode}
|
|
417
|
+
onNavigate={() => {}}
|
|
418
|
+
/>,
|
|
419
|
+
);
|
|
420
|
+
|
|
421
|
+
const restoreButton = screen.getByTitle("Timeline");
|
|
422
|
+
fireEvent.click(restoreButton);
|
|
423
|
+
|
|
424
|
+
expect(screen.queryByRole("slider")).toBeNull();
|
|
425
|
+
});
|
|
426
|
+
|
|
427
|
+
it("should handle multiple property changes at different times", async () => {
|
|
428
|
+
const value = co
|
|
429
|
+
.map({
|
|
430
|
+
a: z.string(),
|
|
431
|
+
b: z.string(),
|
|
432
|
+
c: z.string(),
|
|
433
|
+
})
|
|
434
|
+
.create({ a: "1", b: "2", c: "3" }, account);
|
|
435
|
+
|
|
436
|
+
await sleep(2);
|
|
437
|
+
value.$jazz.set("a", "4");
|
|
438
|
+
await sleep(2);
|
|
439
|
+
value.$jazz.set("b", "5");
|
|
440
|
+
await sleep(2);
|
|
441
|
+
value.$jazz.set("c", "6");
|
|
442
|
+
|
|
443
|
+
const data = value.$jazz.raw.toJSON() as JsonObject;
|
|
444
|
+
|
|
445
|
+
render(
|
|
446
|
+
<CoMapView
|
|
447
|
+
coValue={value.$jazz.raw}
|
|
448
|
+
data={data}
|
|
449
|
+
node={account.$jazz.localNode}
|
|
450
|
+
onNavigate={() => {}}
|
|
451
|
+
/>,
|
|
452
|
+
);
|
|
453
|
+
|
|
454
|
+
const restoreButton = screen.getByTitle("Timeline");
|
|
455
|
+
fireEvent.click(restoreButton);
|
|
456
|
+
|
|
457
|
+
const slider = screen.getByRole("slider") as HTMLInputElement;
|
|
458
|
+
|
|
459
|
+
// Verify we can navigate to initial state
|
|
460
|
+
fireEvent.change(slider, { target: { value: 0 } });
|
|
461
|
+
await waitFor(() => {
|
|
462
|
+
const preview = screen.getByText(/State at that time:/);
|
|
463
|
+
const previewPre = preview.parentElement?.querySelector("pre");
|
|
464
|
+
expect(previewPre?.textContent).toContain("1");
|
|
465
|
+
expect(previewPre?.textContent).toContain("2");
|
|
466
|
+
expect(previewPre?.textContent).toContain("3");
|
|
467
|
+
});
|
|
468
|
+
|
|
469
|
+
// Verify we can navigate to latest state
|
|
470
|
+
fireEvent.change(slider, { target: { value: slider.max } });
|
|
471
|
+
await waitFor(() => {
|
|
472
|
+
const preview = screen.getByText(/State at that time:/);
|
|
473
|
+
const previewPre = preview.parentElement?.querySelector("pre");
|
|
474
|
+
expect(previewPre?.textContent).toContain("4");
|
|
475
|
+
expect(previewPre?.textContent).toContain("5");
|
|
476
|
+
expect(previewPre?.textContent).toContain("6");
|
|
477
|
+
});
|
|
478
|
+
});
|
|
479
|
+
});
|
|
480
|
+
|
|
481
|
+
describe("Preview Display", () => {
|
|
482
|
+
it("should show JSON preview of selected state", async () => {
|
|
483
|
+
const value = co
|
|
484
|
+
.map({
|
|
485
|
+
pet: z.string(),
|
|
486
|
+
age: z.number(),
|
|
487
|
+
})
|
|
488
|
+
.create({ pet: "dog", age: 10 }, account);
|
|
489
|
+
|
|
490
|
+
// wait to have different timestamps in transactions
|
|
491
|
+
await sleep(2);
|
|
492
|
+
|
|
493
|
+
value.$jazz.set("pet", "cat");
|
|
494
|
+
|
|
495
|
+
const data = value.$jazz.raw.toJSON() as JsonObject;
|
|
496
|
+
|
|
497
|
+
render(
|
|
498
|
+
<CoMapView
|
|
499
|
+
coValue={value.$jazz.raw}
|
|
500
|
+
data={data}
|
|
501
|
+
node={account.$jazz.localNode}
|
|
502
|
+
onNavigate={() => {}}
|
|
503
|
+
/>,
|
|
504
|
+
);
|
|
505
|
+
|
|
506
|
+
const restoreButton = screen.getByTitle("Timeline");
|
|
507
|
+
fireEvent.click(restoreButton);
|
|
508
|
+
|
|
509
|
+
// Modal starts at most recent timestamp
|
|
510
|
+
const preview = screen.getByText(/State at that time:/);
|
|
511
|
+
await waitFor(() => {
|
|
512
|
+
const previewPre = preview.parentElement?.querySelector("pre");
|
|
513
|
+
expect(previewPre?.textContent).toContain("cat");
|
|
514
|
+
});
|
|
515
|
+
|
|
516
|
+
const slider = screen.getByRole("slider") as HTMLInputElement;
|
|
517
|
+
fireEvent.change(slider, { target: { value: "0" } });
|
|
518
|
+
|
|
519
|
+
await waitFor(
|
|
520
|
+
() => {
|
|
521
|
+
const previewPre = preview.parentElement?.querySelector("pre");
|
|
522
|
+
expect(previewPre?.textContent).toContain("dog");
|
|
523
|
+
},
|
|
524
|
+
{
|
|
525
|
+
timeout: 1000,
|
|
526
|
+
},
|
|
527
|
+
);
|
|
528
|
+
});
|
|
529
|
+
|
|
530
|
+
it("should update preview when slider moves", async () => {
|
|
531
|
+
const value = co
|
|
532
|
+
.map({
|
|
533
|
+
counter: z.number(),
|
|
534
|
+
})
|
|
535
|
+
.create({ counter: 1 }, account);
|
|
536
|
+
|
|
537
|
+
// wait to have different timestamps in transactions
|
|
538
|
+
await sleep(2);
|
|
539
|
+
value.$jazz.set("counter", 2);
|
|
540
|
+
await sleep(2);
|
|
541
|
+
value.$jazz.set("counter", 3);
|
|
542
|
+
await sleep(2);
|
|
543
|
+
value.$jazz.set("counter", 4);
|
|
544
|
+
|
|
545
|
+
const data = value.$jazz.raw.toJSON() as JsonObject;
|
|
546
|
+
|
|
547
|
+
render(
|
|
548
|
+
<CoMapView
|
|
549
|
+
coValue={value.$jazz.raw}
|
|
550
|
+
data={data}
|
|
551
|
+
node={account.$jazz.localNode}
|
|
552
|
+
onNavigate={() => {}}
|
|
553
|
+
/>,
|
|
554
|
+
);
|
|
555
|
+
|
|
556
|
+
const restoreButton = screen.getByTitle("Timeline");
|
|
557
|
+
fireEvent.click(restoreButton);
|
|
558
|
+
|
|
559
|
+
const slider = screen.getByRole("slider") as HTMLInputElement;
|
|
560
|
+
const preview = screen.getByText(/State at that time:/);
|
|
561
|
+
|
|
562
|
+
// Modal starts at most recent timestamp (counter: 4)
|
|
563
|
+
await waitFor(() => {
|
|
564
|
+
const previewPre = preview.parentElement?.querySelector("pre");
|
|
565
|
+
expect(previewPre?.textContent).toContain('"counter": 4');
|
|
566
|
+
});
|
|
567
|
+
|
|
568
|
+
fireEvent.change(slider, { target: { value: "0" } });
|
|
569
|
+
await waitFor(() => {
|
|
570
|
+
const previewPre = preview.parentElement?.querySelector("pre");
|
|
571
|
+
expect(previewPre?.textContent).toContain('"counter": 1');
|
|
572
|
+
});
|
|
573
|
+
|
|
574
|
+
fireEvent.change(slider, { target: { value: "2" } });
|
|
575
|
+
await waitFor(() => {
|
|
576
|
+
const previewPre = preview.parentElement?.querySelector("pre");
|
|
577
|
+
expect(previewPre?.textContent).toContain('"counter": 3');
|
|
578
|
+
});
|
|
579
|
+
});
|
|
580
|
+
});
|
|
581
|
+
});
|
|
@@ -1,13 +1,19 @@
|
|
|
1
1
|
import { CautionIcon } from "./icons/caution.js";
|
|
2
2
|
import { ChevronDownIcon } from "./icons/chevron-down-icon.js";
|
|
3
3
|
import { DeleteIcon } from "./icons/delete-icon.js";
|
|
4
|
+
import { EditIcon } from "./icons/edit-icon.js";
|
|
4
5
|
import { LinkIcon } from "./icons/link-icon.js";
|
|
6
|
+
import { HistoryIcon } from "./icons/history.js";
|
|
7
|
+
import { AddIcon } from "./icons/add-icon.js";
|
|
5
8
|
|
|
6
9
|
const icons = {
|
|
7
10
|
caution: CautionIcon,
|
|
8
11
|
chevronDown: ChevronDownIcon,
|
|
9
12
|
delete: DeleteIcon,
|
|
13
|
+
edit: EditIcon,
|
|
10
14
|
link: LinkIcon,
|
|
15
|
+
history: HistoryIcon,
|
|
16
|
+
add: AddIcon,
|
|
11
17
|
};
|
|
12
18
|
|
|
13
19
|
// copied from tailwind line height https://tailwindcss.com/docs/font-size
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export function AddIcon(props: React.SVGProps<SVGSVGElement>) {
|
|
2
|
+
return (
|
|
3
|
+
<svg
|
|
4
|
+
{...props}
|
|
5
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
6
|
+
width="24"
|
|
7
|
+
height="24"
|
|
8
|
+
viewBox="0 0 24 24"
|
|
9
|
+
fill="none"
|
|
10
|
+
stroke="currentColor"
|
|
11
|
+
strokeLinejoin="round"
|
|
12
|
+
>
|
|
13
|
+
<path
|
|
14
|
+
d="M4 12H20M12 4V20"
|
|
15
|
+
stroke-width="2"
|
|
16
|
+
stroke-linecap="round"
|
|
17
|
+
stroke-linejoin="round"
|
|
18
|
+
/>
|
|
19
|
+
</svg>
|
|
20
|
+
);
|
|
21
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export function EditIcon(props: React.SVGProps<SVGSVGElement>) {
|
|
2
|
+
return (
|
|
3
|
+
<svg
|
|
4
|
+
{...props}
|
|
5
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
6
|
+
width="24"
|
|
7
|
+
height="24"
|
|
8
|
+
viewBox="0 0 24 24"
|
|
9
|
+
fill="none"
|
|
10
|
+
stroke="currentColor"
|
|
11
|
+
strokeLinejoin="round"
|
|
12
|
+
className="lucide lucide-edit lucide-pencil"
|
|
13
|
+
>
|
|
14
|
+
<path d="M13.2942 7.95881C13.5533 7.63559 13.5013 7.16358 13.178 6.90453C12.8548 6.64549 12.3828 6.6975 12.1238 7.02072L13.2942 7.95881ZM6.811 14.8488L7.37903 15.3385C7.38489 15.3317 7.39062 15.3248 7.39623 15.3178L6.811 14.8488ZM6.64 15.2668L5.89146 15.2179L5.8908 15.2321L6.64 15.2668ZM6.5 18.2898L5.7508 18.2551C5.74908 18.2923 5.75013 18.3296 5.75396 18.3667L6.5 18.2898ZM7.287 18.9768L7.31152 19.7264C7.36154 19.7247 7.41126 19.7181 7.45996 19.7065L7.287 18.9768ZM10.287 18.2658L10.46 18.9956L10.4716 18.9927L10.287 18.2658ZM10.672 18.0218L11.2506 18.4991L11.2571 18.491L10.672 18.0218ZM17.2971 10.959C17.5562 10.6358 17.5043 10.1638 17.1812 9.90466C16.8581 9.64552 16.386 9.69742 16.1269 10.0206L17.2971 10.959ZM12.1269 7.02052C11.8678 7.34365 11.9196 7.81568 12.2428 8.07484C12.5659 8.33399 13.0379 8.28213 13.2971 7.95901L12.1269 7.02052ZM14.3 5.50976L14.8851 5.97901C14.8949 5.96672 14.9044 5.95412 14.9135 5.94123L14.3 5.50976ZM15.929 5.18976L16.4088 4.61332C16.3849 4.59344 16.3598 4.57507 16.3337 4.5583L15.929 5.18976ZM18.166 7.05176L18.6968 6.52192C18.6805 6.50561 18.6635 6.49007 18.6458 6.47532L18.166 7.05176ZM18.5029 7.87264L19.2529 7.87676V7.87676L18.5029 7.87264ZM18.157 8.68976L17.632 8.15412C17.6108 8.17496 17.5908 8.19704 17.5721 8.22025L18.157 8.68976ZM16.1271 10.0203C15.8678 10.3433 15.9195 10.8153 16.2425 11.0746C16.5655 11.3339 17.0376 11.2823 17.2969 10.9593L16.1271 10.0203ZM13.4537 7.37862C13.3923 6.96898 13.0105 6.68666 12.6009 6.74805C12.1912 6.80943 11.9089 7.19127 11.9703 7.60091L13.4537 7.37862ZM16.813 11.2329C17.2234 11.1772 17.5109 10.7992 17.4552 10.3888C17.3994 9.97834 17.0215 9.69082 16.611 9.74659L16.813 11.2329ZM12.1238 7.02072L6.22577 14.3797L7.39623 15.3178L13.2942 7.95881L12.1238 7.02072ZM6.24297 14.359C6.03561 14.5995 5.91226 14.9011 5.89159 15.218L7.38841 15.3156C7.38786 15.324 7.38457 15.3321 7.37903 15.3385L6.24297 14.359ZM5.8908 15.2321L5.7508 18.2551L7.2492 18.3245L7.3892 15.3015L5.8908 15.2321ZM5.75396 18.3667C5.83563 19.1586 6.51588 19.7524 7.31152 19.7264L7.26248 18.2272C7.25928 18.2273 7.25771 18.2268 7.25669 18.2264C7.25526 18.2259 7.25337 18.2249 7.25144 18.2232C7.2495 18.2215 7.24825 18.2198 7.24754 18.2185C7.24703 18.2175 7.24637 18.216 7.24604 18.2128L5.75396 18.3667ZM7.45996 19.7065L10.46 18.9955L10.114 17.536L7.11404 18.247L7.45996 19.7065ZM10.4716 18.9927C10.7771 18.9151 11.05 18.7422 11.2506 18.499L10.0934 17.5445C10.0958 17.5417 10.0989 17.5397 10.1024 17.5388L10.4716 18.9927ZM11.2571 18.491L17.2971 10.959L16.1269 10.0206L10.0869 17.5526L11.2571 18.491ZM13.2971 7.95901L14.8851 5.97901L13.7149 5.04052L12.1269 7.02052L13.2971 7.95901ZM14.9135 5.94123C15.0521 5.74411 15.3214 5.6912 15.5243 5.82123L16.3337 4.5583C15.4544 3.99484 14.2873 4.2241 13.6865 5.0783L14.9135 5.94123ZM15.4492 5.7662L17.6862 7.6282L18.6458 6.47532L16.4088 4.61332L15.4492 5.7662ZM17.6352 7.58161C17.7111 7.6577 17.7535 7.761 17.7529 7.86852L19.2529 7.87676C19.2557 7.36905 19.0555 6.88127 18.6968 6.52192L17.6352 7.58161ZM17.7529 7.86852C17.7524 7.97604 17.7088 8.07886 17.632 8.15412L18.682 9.22541C19.0446 8.87002 19.2501 8.38447 19.2529 7.87676L17.7529 7.86852ZM17.5721 8.22025L16.1271 10.0203L17.2969 10.9593L18.7419 9.15928L17.5721 8.22025ZM11.9703 7.60091C12.3196 9.93221 14.4771 11.5503 16.813 11.2329L16.611 9.74659C15.0881 9.95352 13.6815 8.89855 13.4537 7.37862L11.9703 7.60091Z" />
|
|
15
|
+
</svg>
|
|
16
|
+
);
|
|
17
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export function HistoryIcon(props: React.SVGProps<SVGSVGElement>) {
|
|
2
|
+
return (
|
|
3
|
+
<svg
|
|
4
|
+
{...props}
|
|
5
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
6
|
+
width="24"
|
|
7
|
+
height="24"
|
|
8
|
+
viewBox="0 0 24 24"
|
|
9
|
+
fill="none"
|
|
10
|
+
stroke="currentColor"
|
|
11
|
+
strokeLinejoin="round"
|
|
12
|
+
className="lucide lucide-trash-icon lucide-trash"
|
|
13
|
+
>
|
|
14
|
+
<path
|
|
15
|
+
d="M5.52786 16.7023C6.6602 18.2608 8.3169 19.3584 10.1936 19.7934C12.0703 20.2284 14.0409 19.9716 15.7434 19.0701C17.446 18.1687 18.766 16.6832 19.4611 14.8865C20.1562 13.0898 20.1796 11.1027 19.527 9.29011C18.8745 7.47756 17.5898 5.96135 15.909 5.02005C14.2282 4.07875 12.2641 3.77558 10.3777 4.16623C8.49129 4.55689 6.80919 5.61514 5.64045 7.14656C4.47171 8.67797 3.89482 10.5797 4.01579 12.5023M4.01579 12.5023L2.51579 11.0023M4.01579 12.5023L5.51579 11.0023"
|
|
16
|
+
strokeWidth="2"
|
|
17
|
+
strokeLinecap="round"
|
|
18
|
+
strokeLinejoin="round"
|
|
19
|
+
/>
|
|
20
|
+
<path
|
|
21
|
+
d="M12 8V12L15 15"
|
|
22
|
+
strokeWidth="2"
|
|
23
|
+
strokeLinecap="round"
|
|
24
|
+
strokeLinejoin="round"
|
|
25
|
+
/>
|
|
26
|
+
</svg>
|
|
27
|
+
);
|
|
28
|
+
}
|
|
@@ -28,7 +28,7 @@ const ModalContent = styled("dialog")`
|
|
|
28
28
|
&::backdrop {
|
|
29
29
|
background-color: rgba(0, 0, 0, 0.7);
|
|
30
30
|
}
|
|
31
|
-
|
|
31
|
+
|
|
32
32
|
`;
|
|
33
33
|
|
|
34
34
|
const ModalHeader = styled("div")`
|
|
@@ -65,11 +65,11 @@ const CloseButton = styled("button")`
|
|
|
65
65
|
justify-content: center;
|
|
66
66
|
min-width: 2rem;
|
|
67
67
|
min-height: 2rem;
|
|
68
|
-
|
|
68
|
+
|
|
69
69
|
&:hover {
|
|
70
70
|
background-color: var(--j-foreground);
|
|
71
71
|
}
|
|
72
|
-
|
|
72
|
+
|
|
73
73
|
&:focus-visible {
|
|
74
74
|
outline: 2px solid var(--j-border-focus);
|
|
75
75
|
outline-offset: 2px;
|