rask-ui 0.1.0 → 0.2.0
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/dist/component.d.ts +17 -0
- package/dist/component.d.ts.map +1 -1
- package/dist/component.js +4 -2
- package/dist/createAsync.d.ts +23 -0
- package/dist/createAsync.d.ts.map +1 -1
- package/dist/createAsync.js +23 -0
- package/dist/createAsync.test.d.ts +2 -0
- package/dist/createAsync.test.d.ts.map +1 -0
- package/dist/createAsync.test.js +110 -0
- package/dist/createContext.d.ts +26 -2
- package/dist/createContext.d.ts.map +1 -1
- package/dist/createContext.js +31 -5
- package/dist/createContext.test.d.ts +2 -0
- package/dist/createContext.test.d.ts.map +1 -0
- package/dist/createContext.test.js +136 -0
- package/dist/createMutation.d.ts +23 -0
- package/dist/createMutation.d.ts.map +1 -1
- package/dist/createMutation.js +23 -0
- package/dist/createMutation.test.d.ts +2 -0
- package/dist/createMutation.test.d.ts.map +1 -0
- package/dist/createMutation.test.js +168 -0
- package/dist/createQuery.d.ts +23 -0
- package/dist/createQuery.d.ts.map +1 -1
- package/dist/createQuery.js +23 -0
- package/dist/createQuery.test.d.ts +2 -0
- package/dist/createQuery.test.d.ts.map +1 -0
- package/dist/createQuery.test.js +156 -0
- package/dist/createRef.test.d.ts +2 -0
- package/dist/createRef.test.d.ts.map +1 -0
- package/dist/createRef.test.js +80 -0
- package/dist/createState.d.ts +24 -0
- package/dist/createState.d.ts.map +1 -1
- package/dist/createState.js +24 -0
- package/dist/createState.test.d.ts +2 -0
- package/dist/createState.test.d.ts.map +1 -0
- package/dist/createState.test.js +111 -0
- package/dist/createView.d.ts +54 -0
- package/dist/createView.d.ts.map +1 -0
- package/dist/createView.js +68 -0
- package/dist/createView.test.d.ts +2 -0
- package/dist/createView.test.d.ts.map +1 -0
- package/dist/createView.test.js +203 -0
- package/dist/error.d.ts.map +1 -1
- package/dist/error.js +15 -2
- package/dist/error.test.d.ts +2 -0
- package/dist/error.test.d.ts.map +1 -0
- package/dist/error.test.js +144 -0
- package/dist/index.d.ts +3 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -2
- package/dist/integration.test.d.ts +2 -0
- package/dist/integration.test.d.ts.map +1 -0
- package/dist/integration.test.js +155 -0
- package/dist/jsx-dev-runtime.d.ts +3 -3
- package/dist/jsx-dev-runtime.d.ts.map +1 -1
- package/dist/jsx-dev-runtime.js +2 -2
- package/dist/jsx-runtime.d.ts +1 -4
- package/dist/jsx-runtime.d.ts.map +1 -1
- package/dist/jsx-runtime.js +3 -14
- package/dist/observation.d.ts +1 -0
- package/dist/observation.d.ts.map +1 -1
- package/dist/observation.js +5 -0
- package/dist/observation.test.d.ts +2 -0
- package/dist/observation.test.d.ts.map +1 -0
- package/dist/observation.test.js +150 -0
- package/dist/render-test.d.ts +2 -0
- package/dist/render-test.d.ts.map +1 -0
- package/dist/render-test.js +21 -0
- package/dist/render.d.ts +1 -1
- package/dist/render.d.ts.map +1 -1
- package/dist/render.js +13 -1
- package/dist/test-setup.d.ts +16 -0
- package/dist/test-setup.d.ts.map +1 -0
- package/dist/test-setup.js +39 -0
- package/dist/tests/class.test.d.ts +2 -0
- package/dist/tests/class.test.d.ts.map +1 -0
- package/dist/tests/class.test.js +143 -0
- package/dist/tests/complex-rendering.test.d.ts +2 -0
- package/dist/tests/complex-rendering.test.d.ts.map +1 -0
- package/dist/tests/complex-rendering.test.js +400 -0
- package/dist/tests/component.cleanup.test.d.ts +2 -0
- package/dist/tests/component.cleanup.test.d.ts.map +1 -0
- package/dist/tests/component.cleanup.test.js +325 -0
- package/dist/tests/component.counter.test.d.ts +2 -0
- package/dist/tests/component.counter.test.d.ts.map +1 -0
- package/dist/tests/component.counter.test.js +124 -0
- package/dist/tests/component.interaction.test.d.ts +2 -0
- package/dist/tests/component.interaction.test.d.ts.map +1 -0
- package/dist/tests/component.interaction.test.js +73 -0
- package/dist/tests/component.props.test.d.ts +2 -0
- package/dist/tests/component.props.test.d.ts.map +1 -0
- package/dist/tests/component.props.test.js +88 -0
- package/dist/tests/component.return-types.test.d.ts +2 -0
- package/dist/tests/component.return-types.test.d.ts.map +1 -0
- package/dist/tests/component.return-types.test.js +357 -0
- package/dist/tests/component.state.test.d.ts +2 -0
- package/dist/tests/component.state.test.d.ts.map +1 -0
- package/dist/tests/component.state.test.js +129 -0
- package/dist/tests/component.test.d.ts +2 -0
- package/dist/tests/component.test.d.ts.map +1 -0
- package/dist/tests/component.test.js +63 -0
- package/dist/tests/createAsync.test.d.ts +2 -0
- package/dist/tests/createAsync.test.d.ts.map +1 -0
- package/dist/tests/createAsync.test.js +110 -0
- package/dist/tests/createContext.test.d.ts +2 -0
- package/dist/tests/createContext.test.d.ts.map +1 -0
- package/dist/tests/createContext.test.js +141 -0
- package/dist/tests/createMutation.test.d.ts +2 -0
- package/dist/tests/createMutation.test.d.ts.map +1 -0
- package/dist/tests/createMutation.test.js +168 -0
- package/dist/tests/createQuery.test.d.ts +2 -0
- package/dist/tests/createQuery.test.d.ts.map +1 -0
- package/dist/tests/createQuery.test.js +156 -0
- package/dist/tests/createRef.test.d.ts +2 -0
- package/dist/tests/createRef.test.d.ts.map +1 -0
- package/dist/tests/createRef.test.js +84 -0
- package/dist/tests/createState.test.d.ts +2 -0
- package/dist/tests/createState.test.d.ts.map +1 -0
- package/dist/tests/createState.test.js +111 -0
- package/dist/tests/createView.test.d.ts +2 -0
- package/dist/tests/createView.test.d.ts.map +1 -0
- package/dist/tests/createView.test.js +203 -0
- package/dist/tests/edge-cases.test.d.ts +2 -0
- package/dist/tests/edge-cases.test.d.ts.map +1 -0
- package/dist/tests/edge-cases.test.js +637 -0
- package/dist/tests/error-no-boundary.test.d.ts +2 -0
- package/dist/tests/error-no-boundary.test.d.ts.map +1 -0
- package/dist/tests/error-no-boundary.test.js +174 -0
- package/dist/tests/error.test.d.ts +2 -0
- package/dist/tests/error.test.d.ts.map +1 -0
- package/dist/tests/error.test.js +199 -0
- package/dist/tests/fragment.test.d.ts +2 -0
- package/dist/tests/fragment.test.d.ts.map +1 -0
- package/dist/tests/fragment.test.js +618 -0
- package/dist/tests/integration.test.d.ts +2 -0
- package/dist/tests/integration.test.d.ts.map +1 -0
- package/dist/tests/integration.test.js +192 -0
- package/dist/tests/keys.test.d.ts +2 -0
- package/dist/tests/keys.test.d.ts.map +1 -0
- package/dist/tests/keys.test.js +293 -0
- package/dist/tests/mount.test.d.ts +2 -0
- package/dist/tests/mount.test.d.ts.map +1 -0
- package/dist/tests/mount.test.js +91 -0
- package/dist/tests/observation.test.d.ts +2 -0
- package/dist/tests/observation.test.d.ts.map +1 -0
- package/dist/tests/observation.test.js +150 -0
- package/dist/tests/patch.test.d.ts +2 -0
- package/dist/tests/patch.test.d.ts.map +1 -0
- package/dist/tests/patch.test.js +498 -0
- package/dist/tests/patchChildren.test.d.ts +2 -0
- package/dist/tests/patchChildren.test.d.ts.map +1 -0
- package/dist/tests/patchChildren.test.js +387 -0
- package/dist/tests/primitives.test.d.ts +2 -0
- package/dist/tests/primitives.test.d.ts.map +1 -0
- package/dist/tests/primitives.test.js +132 -0
- package/dist/vdom/AbstractVNode.d.ts +22 -0
- package/dist/vdom/AbstractVNode.d.ts.map +1 -0
- package/dist/vdom/AbstractVNode.js +106 -0
- package/dist/vdom/ComponentVNode.d.ts +48 -0
- package/dist/vdom/ComponentVNode.d.ts.map +1 -0
- package/dist/vdom/ComponentVNode.js +209 -0
- package/dist/vdom/ElementVNode.d.ts +24 -0
- package/dist/vdom/ElementVNode.d.ts.map +1 -0
- package/dist/vdom/ElementVNode.js +126 -0
- package/dist/vdom/FragmentVNode.d.ts +13 -0
- package/dist/vdom/FragmentVNode.d.ts.map +1 -0
- package/dist/vdom/FragmentVNode.js +34 -0
- package/dist/vdom/RootVNode.d.ts +22 -0
- package/dist/vdom/RootVNode.d.ts.map +1 -0
- package/dist/vdom/RootVNode.js +55 -0
- package/dist/vdom/TextVNode.d.ts +11 -0
- package/dist/vdom/TextVNode.d.ts.map +1 -0
- package/dist/vdom/TextVNode.js +32 -0
- package/dist/vdom/class.test.d.ts +2 -0
- package/dist/vdom/class.test.d.ts.map +1 -0
- package/dist/vdom/class.test.js +143 -0
- package/dist/vdom/complex-rendering.test.d.ts +2 -0
- package/dist/vdom/complex-rendering.test.d.ts.map +1 -0
- package/dist/vdom/complex-rendering.test.js +400 -0
- package/dist/vdom/component.cleanup.test.d.ts +2 -0
- package/dist/vdom/component.cleanup.test.d.ts.map +1 -0
- package/dist/vdom/component.cleanup.test.js +323 -0
- package/dist/vdom/component.counter.test.d.ts +2 -0
- package/dist/vdom/component.counter.test.d.ts.map +1 -0
- package/dist/vdom/component.counter.test.js +124 -0
- package/dist/vdom/component.interaction.test.d.ts +2 -0
- package/dist/vdom/component.interaction.test.d.ts.map +1 -0
- package/dist/vdom/component.interaction.test.js +73 -0
- package/dist/vdom/component.props.test.d.ts +2 -0
- package/dist/vdom/component.props.test.d.ts.map +1 -0
- package/dist/vdom/component.props.test.js +88 -0
- package/dist/vdom/component.return-types.test.d.ts +2 -0
- package/dist/vdom/component.return-types.test.d.ts.map +1 -0
- package/dist/vdom/component.return-types.test.js +357 -0
- package/dist/vdom/component.state.test.d.ts +2 -0
- package/dist/vdom/component.state.test.d.ts.map +1 -0
- package/dist/vdom/component.state.test.js +129 -0
- package/dist/vdom/component.test.d.ts +2 -0
- package/dist/vdom/component.test.d.ts.map +1 -0
- package/dist/vdom/component.test.js +63 -0
- package/dist/vdom/dom-utils.d.ts +9 -0
- package/dist/vdom/dom-utils.d.ts.map +1 -0
- package/dist/vdom/dom-utils.js +74 -0
- package/dist/vdom/edge-cases.test.d.ts +2 -0
- package/dist/vdom/edge-cases.test.d.ts.map +1 -0
- package/dist/vdom/edge-cases.test.js +637 -0
- package/dist/vdom/fragment.test.d.ts +2 -0
- package/dist/vdom/fragment.test.d.ts.map +1 -0
- package/dist/vdom/fragment.test.js +618 -0
- package/dist/vdom/index.d.ts +10 -0
- package/dist/vdom/index.d.ts.map +1 -0
- package/dist/vdom/index.js +26 -0
- package/dist/vdom/keys.test.d.ts +2 -0
- package/dist/vdom/keys.test.d.ts.map +1 -0
- package/dist/vdom/keys.test.js +293 -0
- package/dist/vdom/mount.test.d.ts +2 -0
- package/dist/vdom/mount.test.d.ts.map +1 -0
- package/dist/vdom/mount.test.js +91 -0
- package/dist/vdom/patch.test.d.ts +2 -0
- package/dist/vdom/patch.test.d.ts.map +1 -0
- package/dist/vdom/patch.test.js +498 -0
- package/dist/vdom/patchChildren.test.d.ts +2 -0
- package/dist/vdom/patchChildren.test.d.ts.map +1 -0
- package/dist/vdom/patchChildren.test.js +392 -0
- package/dist/vdom/primitives.test.d.ts +2 -0
- package/dist/vdom/primitives.test.d.ts.map +1 -0
- package/dist/vdom/primitives.test.js +132 -0
- package/dist/vdom/types.d.ts +8 -0
- package/dist/vdom/types.d.ts.map +1 -0
- package/dist/vdom/types.js +1 -0
- package/dist/vdom/utils.d.ts +6 -0
- package/dist/vdom/utils.d.ts.map +1 -0
- package/dist/vdom/utils.js +63 -0
- package/package.json +1 -4
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import { jsx, render } from "./index";
|
|
3
|
+
import { createState } from "../createState";
|
|
4
|
+
const waitForUpdate = () => new Promise((resolve) => setTimeout(resolve, 10));
|
|
5
|
+
describe("VDOM Keys", () => {
|
|
6
|
+
it("should preserve elements when reordering with keys", async () => {
|
|
7
|
+
const container = document.createElement("div");
|
|
8
|
+
let stateFn;
|
|
9
|
+
const App = () => {
|
|
10
|
+
const state = createState({ order: ["a", "b", "c"] });
|
|
11
|
+
stateFn = state;
|
|
12
|
+
return () => jsx("ul", {
|
|
13
|
+
children: state.order.map((key) => jsx("li", { children: `Item ${key.toUpperCase()}` }, key)),
|
|
14
|
+
});
|
|
15
|
+
};
|
|
16
|
+
render(jsx(App, {}), container);
|
|
17
|
+
const ul = container.children[0];
|
|
18
|
+
const initialChildren = Array.from(ul.children);
|
|
19
|
+
// Store references to the original DOM elements
|
|
20
|
+
const itemA = initialChildren[0];
|
|
21
|
+
const itemB = initialChildren[1];
|
|
22
|
+
const itemC = initialChildren[2];
|
|
23
|
+
expect(itemA.textContent).toBe("Item A");
|
|
24
|
+
expect(itemB.textContent).toBe("Item B");
|
|
25
|
+
expect(itemC.textContent).toBe("Item C");
|
|
26
|
+
// Reorder to C, A, B
|
|
27
|
+
stateFn.order = ["c", "a", "b"];
|
|
28
|
+
await waitForUpdate();
|
|
29
|
+
const newChildren = Array.from(ul.children);
|
|
30
|
+
// Verify elements were reordered, not recreated
|
|
31
|
+
// The DOM elements should be the same instances, just moved
|
|
32
|
+
expect(newChildren[0]).toBe(itemC); // C is now first
|
|
33
|
+
expect(newChildren[1]).toBe(itemA); // A is now second
|
|
34
|
+
expect(newChildren[2]).toBe(itemB); // B is now third
|
|
35
|
+
expect(newChildren[0].textContent).toBe("Item C");
|
|
36
|
+
expect(newChildren[1].textContent).toBe("Item A");
|
|
37
|
+
expect(newChildren[2].textContent).toBe("Item B");
|
|
38
|
+
});
|
|
39
|
+
it("should not preserve elements when reordering without keys", async () => {
|
|
40
|
+
const container = document.createElement("div");
|
|
41
|
+
let stateFn;
|
|
42
|
+
const App = () => {
|
|
43
|
+
const state = createState({ items: ["Item A", "Item B", "Item C"] });
|
|
44
|
+
stateFn = state;
|
|
45
|
+
return () => jsx("ul", {
|
|
46
|
+
children: state.items.map((text) => jsx("li", { children: text })),
|
|
47
|
+
});
|
|
48
|
+
};
|
|
49
|
+
render(jsx(App, {}), container);
|
|
50
|
+
const ul = container.children[0];
|
|
51
|
+
const initialChildren = Array.from(ul.children);
|
|
52
|
+
// Store references to the original DOM elements
|
|
53
|
+
const itemA = initialChildren[0];
|
|
54
|
+
const itemB = initialChildren[1];
|
|
55
|
+
const itemC = initialChildren[2];
|
|
56
|
+
// Reorder to C, A, B
|
|
57
|
+
stateFn.items = ["Item C", "Item A", "Item B"];
|
|
58
|
+
await waitForUpdate();
|
|
59
|
+
const newChildren = Array.from(ul.children);
|
|
60
|
+
// Without keys, the DOM elements are reused in place and just updated
|
|
61
|
+
// So the DOM nodes remain the same, but their content is changed
|
|
62
|
+
expect(newChildren[0]).toBe(itemA); // Same DOM node, but content changed to "Item C"
|
|
63
|
+
expect(newChildren[1]).toBe(itemB); // Same DOM node, but content changed to "Item A"
|
|
64
|
+
expect(newChildren[2]).toBe(itemC); // Same DOM node, but content changed to "Item B"
|
|
65
|
+
expect(newChildren[0].textContent).toBe("Item C");
|
|
66
|
+
expect(newChildren[1].textContent).toBe("Item A");
|
|
67
|
+
expect(newChildren[2].textContent).toBe("Item B");
|
|
68
|
+
});
|
|
69
|
+
it("should preserve element state when reordering with keys", async () => {
|
|
70
|
+
const container = document.createElement("div");
|
|
71
|
+
let stateFn;
|
|
72
|
+
const App = () => {
|
|
73
|
+
const state = createState({
|
|
74
|
+
order: ["first", "second", "third"],
|
|
75
|
+
values: { first: "First", second: "Second", third: "Third" },
|
|
76
|
+
});
|
|
77
|
+
stateFn = state;
|
|
78
|
+
return () => jsx("div", {
|
|
79
|
+
children: state.order.map((key) => jsx("input", { value: state.values[key] }, key)),
|
|
80
|
+
});
|
|
81
|
+
};
|
|
82
|
+
render(jsx(App, {}), container);
|
|
83
|
+
const div = container.children[0];
|
|
84
|
+
const initialInputs = Array.from(div.children);
|
|
85
|
+
// User "types" in the inputs (simulating user interaction)
|
|
86
|
+
initialInputs[0].value = "Modified First";
|
|
87
|
+
initialInputs[1].value = "Modified Second";
|
|
88
|
+
initialInputs[2].value = "Modified Third";
|
|
89
|
+
// Store references
|
|
90
|
+
const input1 = initialInputs[0];
|
|
91
|
+
const input2 = initialInputs[1];
|
|
92
|
+
const input3 = initialInputs[2];
|
|
93
|
+
// Reorder to third, first, second
|
|
94
|
+
stateFn.order = ["third", "first", "second"];
|
|
95
|
+
await waitForUpdate();
|
|
96
|
+
const newInputs = Array.from(div.children);
|
|
97
|
+
// Verify elements were moved (not recreated) so user modifications are preserved
|
|
98
|
+
expect(newInputs[0]).toBe(input3);
|
|
99
|
+
expect(newInputs[1]).toBe(input1);
|
|
100
|
+
expect(newInputs[2]).toBe(input2);
|
|
101
|
+
// The user's modifications should be preserved because the DOM elements were moved
|
|
102
|
+
expect(newInputs[0].value).toBe("Modified Third");
|
|
103
|
+
expect(newInputs[1].value).toBe("Modified First");
|
|
104
|
+
expect(newInputs[2].value).toBe("Modified Second");
|
|
105
|
+
});
|
|
106
|
+
it("should handle adding new items with keys", async () => {
|
|
107
|
+
const container = document.createElement("div");
|
|
108
|
+
let stateFn;
|
|
109
|
+
const App = () => {
|
|
110
|
+
const state = createState({ items: ["a", "b"] });
|
|
111
|
+
stateFn = state;
|
|
112
|
+
return () => jsx("ul", {
|
|
113
|
+
children: state.items.map((key) => jsx("li", { children: `Item ${key.toUpperCase()}` }, key)),
|
|
114
|
+
});
|
|
115
|
+
};
|
|
116
|
+
render(jsx(App, {}), container);
|
|
117
|
+
const ul = container.children[0];
|
|
118
|
+
const initialChildren = Array.from(ul.children);
|
|
119
|
+
expect(ul.children.length).toBe(2);
|
|
120
|
+
// Store references
|
|
121
|
+
const itemA = initialChildren[0];
|
|
122
|
+
const itemB = initialChildren[1];
|
|
123
|
+
// Add a new item in the middle
|
|
124
|
+
stateFn.items = ["a", "c", "b"];
|
|
125
|
+
await waitForUpdate();
|
|
126
|
+
const newChildren = Array.from(ul.children);
|
|
127
|
+
expect(ul.children.length).toBe(3);
|
|
128
|
+
// Original elements should be preserved
|
|
129
|
+
expect(newChildren[0]).toBe(itemA);
|
|
130
|
+
expect(newChildren[2]).toBe(itemB);
|
|
131
|
+
// New element in the middle
|
|
132
|
+
expect(newChildren[1]).not.toBe(itemA);
|
|
133
|
+
expect(newChildren[1]).not.toBe(itemB);
|
|
134
|
+
expect(newChildren[1].textContent).toBe("Item C");
|
|
135
|
+
});
|
|
136
|
+
it("should handle removing items with keys", async () => {
|
|
137
|
+
const container = document.createElement("div");
|
|
138
|
+
let stateFn;
|
|
139
|
+
const App = () => {
|
|
140
|
+
const state = createState({ items: ["a", "b", "c"] });
|
|
141
|
+
stateFn = state;
|
|
142
|
+
return () => jsx("ul", {
|
|
143
|
+
children: state.items.map((key) => jsx("li", { children: `Item ${key.toUpperCase()}` }, key)),
|
|
144
|
+
});
|
|
145
|
+
};
|
|
146
|
+
render(jsx(App, {}), container);
|
|
147
|
+
const ul = container.children[0];
|
|
148
|
+
const initialChildren = Array.from(ul.children);
|
|
149
|
+
expect(ul.children.length).toBe(3);
|
|
150
|
+
// Store references
|
|
151
|
+
const itemA = initialChildren[0];
|
|
152
|
+
const itemC = initialChildren[2];
|
|
153
|
+
// Remove the middle item
|
|
154
|
+
stateFn.items = ["a", "c"];
|
|
155
|
+
await waitForUpdate();
|
|
156
|
+
const newChildren = Array.from(ul.children);
|
|
157
|
+
expect(ul.children.length).toBe(2);
|
|
158
|
+
// Remaining elements should be preserved
|
|
159
|
+
expect(newChildren[0]).toBe(itemA);
|
|
160
|
+
expect(newChildren[1]).toBe(itemC);
|
|
161
|
+
});
|
|
162
|
+
it("should handle replacing all items with different keys", async () => {
|
|
163
|
+
const container = document.createElement("div");
|
|
164
|
+
let stateFn;
|
|
165
|
+
const App = () => {
|
|
166
|
+
const state = createState({ items: ["a", "b", "c"] });
|
|
167
|
+
stateFn = state;
|
|
168
|
+
return () => jsx("ul", {
|
|
169
|
+
children: state.items.map((key) => jsx("li", { children: `Item ${key.toUpperCase()}` }, key)),
|
|
170
|
+
});
|
|
171
|
+
};
|
|
172
|
+
render(jsx(App, {}), container);
|
|
173
|
+
const ul = container.children[0];
|
|
174
|
+
const initialChildren = Array.from(ul.children);
|
|
175
|
+
// Store references
|
|
176
|
+
const itemA = initialChildren[0];
|
|
177
|
+
const itemB = initialChildren[1];
|
|
178
|
+
const itemC = initialChildren[2];
|
|
179
|
+
// Replace all items with new keys
|
|
180
|
+
stateFn.items = ["x", "y", "z"];
|
|
181
|
+
await waitForUpdate();
|
|
182
|
+
const newChildren = Array.from(ul.children);
|
|
183
|
+
expect(ul.children.length).toBe(3);
|
|
184
|
+
// All elements should be new instances
|
|
185
|
+
expect(newChildren[0]).not.toBe(itemA);
|
|
186
|
+
expect(newChildren[1]).not.toBe(itemB);
|
|
187
|
+
expect(newChildren[2]).not.toBe(itemC);
|
|
188
|
+
expect(newChildren[0].textContent).toBe("Item X");
|
|
189
|
+
expect(newChildren[1].textContent).toBe("Item Y");
|
|
190
|
+
expect(newChildren[2].textContent).toBe("Item Z");
|
|
191
|
+
});
|
|
192
|
+
it("should handle complex reordering scenario with keys", async () => {
|
|
193
|
+
const container = document.createElement("div");
|
|
194
|
+
let stateFn;
|
|
195
|
+
const App = () => {
|
|
196
|
+
const state = createState({ items: ["a", "b", "c", "d", "e"] });
|
|
197
|
+
stateFn = state;
|
|
198
|
+
return () => jsx("ul", {
|
|
199
|
+
children: state.items.map((key) => jsx("li", { children: key.toUpperCase() }, key)),
|
|
200
|
+
});
|
|
201
|
+
};
|
|
202
|
+
render(jsx(App, {}), container);
|
|
203
|
+
const ul = container.children[0];
|
|
204
|
+
const initialChildren = Array.from(ul.children);
|
|
205
|
+
const [itemA, itemB, itemC, itemD, itemE] = initialChildren;
|
|
206
|
+
// Complex reorder: remove C, add F, reorder to [E, A, F, D, B]
|
|
207
|
+
stateFn.items = ["e", "a", "f", "d", "b"];
|
|
208
|
+
await waitForUpdate();
|
|
209
|
+
const newChildren = Array.from(ul.children);
|
|
210
|
+
expect(ul.children.length).toBe(5);
|
|
211
|
+
// Verify preserved elements are in correct positions
|
|
212
|
+
expect(newChildren[0]).toBe(itemE);
|
|
213
|
+
expect(newChildren[1]).toBe(itemA);
|
|
214
|
+
expect(newChildren[3]).toBe(itemD);
|
|
215
|
+
expect(newChildren[4]).toBe(itemB);
|
|
216
|
+
// F is new
|
|
217
|
+
expect(newChildren[2]).not.toBe(itemA);
|
|
218
|
+
expect(newChildren[2]).not.toBe(itemB);
|
|
219
|
+
expect(newChildren[2]).not.toBe(itemC);
|
|
220
|
+
expect(newChildren[2]).not.toBe(itemD);
|
|
221
|
+
expect(newChildren[2]).not.toBe(itemE);
|
|
222
|
+
expect(newChildren[2].textContent).toBe("F");
|
|
223
|
+
// C should not be in the list
|
|
224
|
+
expect(newChildren.includes(itemC)).toBe(false);
|
|
225
|
+
});
|
|
226
|
+
it("should handle nested elements with keys", async () => {
|
|
227
|
+
const container = document.createElement("div");
|
|
228
|
+
let stateFn;
|
|
229
|
+
const App = () => {
|
|
230
|
+
const state = createState({ order: ["section1", "section2"] });
|
|
231
|
+
stateFn = state;
|
|
232
|
+
return () => jsx("div", {
|
|
233
|
+
children: state.order.map((key) => {
|
|
234
|
+
const num = key === "section1" ? "1" : "2";
|
|
235
|
+
return jsx("section", {
|
|
236
|
+
children: [
|
|
237
|
+
jsx("h2", { children: `Section ${num}` }),
|
|
238
|
+
jsx("p", { children: `Content ${num}` }),
|
|
239
|
+
],
|
|
240
|
+
}, key);
|
|
241
|
+
}),
|
|
242
|
+
});
|
|
243
|
+
};
|
|
244
|
+
render(jsx(App, {}), container);
|
|
245
|
+
const div = container.children[0];
|
|
246
|
+
const initialSections = Array.from(div.children);
|
|
247
|
+
const section1 = initialSections[0];
|
|
248
|
+
const section2 = initialSections[1];
|
|
249
|
+
// Reorder sections
|
|
250
|
+
stateFn.order = ["section2", "section1"];
|
|
251
|
+
await waitForUpdate();
|
|
252
|
+
const newSections = Array.from(div.children);
|
|
253
|
+
// Verify sections were moved, not recreated
|
|
254
|
+
expect(newSections[0]).toBe(section2);
|
|
255
|
+
expect(newSections[1]).toBe(section1);
|
|
256
|
+
expect(newSections[0].children[0].textContent).toBe("Section 2");
|
|
257
|
+
expect(newSections[1].children[0].textContent).toBe("Section 1");
|
|
258
|
+
});
|
|
259
|
+
it("should handle mixed keys and non-keyed elements", async () => {
|
|
260
|
+
const container = document.createElement("div");
|
|
261
|
+
let stateFn;
|
|
262
|
+
const App = () => {
|
|
263
|
+
const state = createState({
|
|
264
|
+
items: [
|
|
265
|
+
{ key: "a", text: "Item A" },
|
|
266
|
+
{ text: "Item B" },
|
|
267
|
+
{ key: "c", text: "Item C" },
|
|
268
|
+
],
|
|
269
|
+
});
|
|
270
|
+
stateFn = state;
|
|
271
|
+
return () => jsx("ul", {
|
|
272
|
+
children: state.items.map((item) => jsx("li", { children: item.text }, item.key)),
|
|
273
|
+
});
|
|
274
|
+
};
|
|
275
|
+
render(jsx(App, {}), container);
|
|
276
|
+
const ul = container.children[0];
|
|
277
|
+
expect(ul.children.length).toBe(3);
|
|
278
|
+
expect(ul.children[0].textContent).toBe("Item A");
|
|
279
|
+
expect(ul.children[1].textContent).toBe("Item B");
|
|
280
|
+
expect(ul.children[2].textContent).toBe("Item C");
|
|
281
|
+
// Reorder with mixed keys
|
|
282
|
+
stateFn.items = [
|
|
283
|
+
{ key: "c", text: "Item C" },
|
|
284
|
+
{ text: "Item B Modified" },
|
|
285
|
+
{ key: "a", text: "Item A" },
|
|
286
|
+
];
|
|
287
|
+
await waitForUpdate();
|
|
288
|
+
expect(ul.children.length).toBe(3);
|
|
289
|
+
expect(ul.children[0].textContent).toBe("Item C");
|
|
290
|
+
expect(ul.children[1].textContent).toBe("Item B Modified");
|
|
291
|
+
expect(ul.children[2].textContent).toBe("Item A");
|
|
292
|
+
});
|
|
293
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mount.test.d.ts","sourceRoot":"","sources":["../../src/vdom/mount.test.tsx"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { describe, it, expect, vi } from "vitest";
|
|
2
|
+
import { jsx, render } from "./index";
|
|
3
|
+
describe("VDOM Mount", () => {
|
|
4
|
+
it("should render a single element to the DOM", () => {
|
|
5
|
+
// Create a container element
|
|
6
|
+
const container = document.createElement("div");
|
|
7
|
+
// Create a vnode using jsx
|
|
8
|
+
const vnode = jsx("div", { id: "test" });
|
|
9
|
+
// Render the vnode to the container
|
|
10
|
+
render(vnode, container);
|
|
11
|
+
// Assert that the element was created and appended
|
|
12
|
+
expect(container.children.length).toBe(1);
|
|
13
|
+
expect(container.children[0].tagName).toBe("DIV");
|
|
14
|
+
});
|
|
15
|
+
it("should render an element with multiple children", () => {
|
|
16
|
+
// Create a container element
|
|
17
|
+
const container = document.createElement("div");
|
|
18
|
+
// Create child vnodes
|
|
19
|
+
const child1 = jsx("span", { id: "child1" });
|
|
20
|
+
const child2 = jsx("span", { id: "child2" });
|
|
21
|
+
const child3 = jsx("div", { id: "child3" });
|
|
22
|
+
// Create parent vnode with children
|
|
23
|
+
const parentVNode = jsx("div", {
|
|
24
|
+
id: "parent",
|
|
25
|
+
children: [child1, child2, child3],
|
|
26
|
+
});
|
|
27
|
+
// Render the parent vnode
|
|
28
|
+
render(parentVNode, container);
|
|
29
|
+
// Verify parent was rendered
|
|
30
|
+
expect(container.children.length).toBe(1);
|
|
31
|
+
expect(container.children[0].tagName).toBe("DIV");
|
|
32
|
+
// Verify children were rendered
|
|
33
|
+
const parent = container.children[0];
|
|
34
|
+
expect(parent.children.length).toBe(3);
|
|
35
|
+
expect(parent.children[0].tagName).toBe("SPAN");
|
|
36
|
+
expect(parent.children[1].tagName).toBe("SPAN");
|
|
37
|
+
expect(parent.children[2].tagName).toBe("DIV");
|
|
38
|
+
});
|
|
39
|
+
it("should render element with data and aria attributes", () => {
|
|
40
|
+
// Create a container element
|
|
41
|
+
const container = document.createElement("div");
|
|
42
|
+
// Create vnode with data and aria attributes
|
|
43
|
+
const vnode = jsx("div", {
|
|
44
|
+
"data-testid": "test-component",
|
|
45
|
+
"data-value": "123",
|
|
46
|
+
"aria-label": "Test element",
|
|
47
|
+
"aria-hidden": "true",
|
|
48
|
+
});
|
|
49
|
+
// Render the vnode
|
|
50
|
+
render(vnode, container);
|
|
51
|
+
// Verify attributes were set
|
|
52
|
+
const div = container.children[0];
|
|
53
|
+
expect(div.getAttribute("data-testid")).toBe("test-component");
|
|
54
|
+
expect(div.getAttribute("data-value")).toBe("123");
|
|
55
|
+
expect(div.getAttribute("aria-label")).toBe("Test element");
|
|
56
|
+
expect(div.getAttribute("aria-hidden")).toBe("true");
|
|
57
|
+
});
|
|
58
|
+
it("should render element with properties", () => {
|
|
59
|
+
// Create a container element
|
|
60
|
+
const container = document.createElement("div");
|
|
61
|
+
// Create vnode with properties
|
|
62
|
+
const vnode = jsx("div", {
|
|
63
|
+
className: "test-class",
|
|
64
|
+
textContent: "Hello World",
|
|
65
|
+
});
|
|
66
|
+
// Render the vnode
|
|
67
|
+
render(vnode, container);
|
|
68
|
+
// Verify properties were set
|
|
69
|
+
const div = container.children[0];
|
|
70
|
+
expect(div.className).toBe("test-class");
|
|
71
|
+
expect(div.textContent).toBe("Hello World");
|
|
72
|
+
});
|
|
73
|
+
it("should render element with event listener", () => {
|
|
74
|
+
// Create a container element
|
|
75
|
+
const container = document.createElement("div");
|
|
76
|
+
// Create mock click handler
|
|
77
|
+
const handleClick = vi.fn();
|
|
78
|
+
// Create vnode with event listener
|
|
79
|
+
const vnode = jsx("button", {
|
|
80
|
+
onClick: handleClick,
|
|
81
|
+
});
|
|
82
|
+
// Render the vnode
|
|
83
|
+
render(vnode, container);
|
|
84
|
+
// Get the button element
|
|
85
|
+
const button = container.children[0];
|
|
86
|
+
// Trigger click event
|
|
87
|
+
button.click();
|
|
88
|
+
// Verify event handler was called
|
|
89
|
+
expect(handleClick).toHaveBeenCalledTimes(1);
|
|
90
|
+
});
|
|
91
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"patch.test.d.ts","sourceRoot":"","sources":["../../src/vdom/patch.test.tsx"],"names":[],"mappings":""}
|