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.
Files changed (234) hide show
  1. package/dist/component.d.ts +17 -0
  2. package/dist/component.d.ts.map +1 -1
  3. package/dist/component.js +4 -2
  4. package/dist/createAsync.d.ts +23 -0
  5. package/dist/createAsync.d.ts.map +1 -1
  6. package/dist/createAsync.js +23 -0
  7. package/dist/createAsync.test.d.ts +2 -0
  8. package/dist/createAsync.test.d.ts.map +1 -0
  9. package/dist/createAsync.test.js +110 -0
  10. package/dist/createContext.d.ts +26 -2
  11. package/dist/createContext.d.ts.map +1 -1
  12. package/dist/createContext.js +31 -5
  13. package/dist/createContext.test.d.ts +2 -0
  14. package/dist/createContext.test.d.ts.map +1 -0
  15. package/dist/createContext.test.js +136 -0
  16. package/dist/createMutation.d.ts +23 -0
  17. package/dist/createMutation.d.ts.map +1 -1
  18. package/dist/createMutation.js +23 -0
  19. package/dist/createMutation.test.d.ts +2 -0
  20. package/dist/createMutation.test.d.ts.map +1 -0
  21. package/dist/createMutation.test.js +168 -0
  22. package/dist/createQuery.d.ts +23 -0
  23. package/dist/createQuery.d.ts.map +1 -1
  24. package/dist/createQuery.js +23 -0
  25. package/dist/createQuery.test.d.ts +2 -0
  26. package/dist/createQuery.test.d.ts.map +1 -0
  27. package/dist/createQuery.test.js +156 -0
  28. package/dist/createRef.test.d.ts +2 -0
  29. package/dist/createRef.test.d.ts.map +1 -0
  30. package/dist/createRef.test.js +80 -0
  31. package/dist/createState.d.ts +24 -0
  32. package/dist/createState.d.ts.map +1 -1
  33. package/dist/createState.js +24 -0
  34. package/dist/createState.test.d.ts +2 -0
  35. package/dist/createState.test.d.ts.map +1 -0
  36. package/dist/createState.test.js +111 -0
  37. package/dist/createView.d.ts +54 -0
  38. package/dist/createView.d.ts.map +1 -0
  39. package/dist/createView.js +68 -0
  40. package/dist/createView.test.d.ts +2 -0
  41. package/dist/createView.test.d.ts.map +1 -0
  42. package/dist/createView.test.js +203 -0
  43. package/dist/error.d.ts.map +1 -1
  44. package/dist/error.js +15 -2
  45. package/dist/error.test.d.ts +2 -0
  46. package/dist/error.test.d.ts.map +1 -0
  47. package/dist/error.test.js +144 -0
  48. package/dist/index.d.ts +3 -2
  49. package/dist/index.d.ts.map +1 -1
  50. package/dist/index.js +3 -2
  51. package/dist/integration.test.d.ts +2 -0
  52. package/dist/integration.test.d.ts.map +1 -0
  53. package/dist/integration.test.js +155 -0
  54. package/dist/jsx-dev-runtime.d.ts +3 -3
  55. package/dist/jsx-dev-runtime.d.ts.map +1 -1
  56. package/dist/jsx-dev-runtime.js +2 -2
  57. package/dist/jsx-runtime.d.ts +1 -4
  58. package/dist/jsx-runtime.d.ts.map +1 -1
  59. package/dist/jsx-runtime.js +3 -14
  60. package/dist/observation.d.ts +1 -0
  61. package/dist/observation.d.ts.map +1 -1
  62. package/dist/observation.js +5 -0
  63. package/dist/observation.test.d.ts +2 -0
  64. package/dist/observation.test.d.ts.map +1 -0
  65. package/dist/observation.test.js +150 -0
  66. package/dist/render-test.d.ts +2 -0
  67. package/dist/render-test.d.ts.map +1 -0
  68. package/dist/render-test.js +21 -0
  69. package/dist/render.d.ts +1 -1
  70. package/dist/render.d.ts.map +1 -1
  71. package/dist/render.js +13 -1
  72. package/dist/test-setup.d.ts +16 -0
  73. package/dist/test-setup.d.ts.map +1 -0
  74. package/dist/test-setup.js +39 -0
  75. package/dist/tests/class.test.d.ts +2 -0
  76. package/dist/tests/class.test.d.ts.map +1 -0
  77. package/dist/tests/class.test.js +143 -0
  78. package/dist/tests/complex-rendering.test.d.ts +2 -0
  79. package/dist/tests/complex-rendering.test.d.ts.map +1 -0
  80. package/dist/tests/complex-rendering.test.js +400 -0
  81. package/dist/tests/component.cleanup.test.d.ts +2 -0
  82. package/dist/tests/component.cleanup.test.d.ts.map +1 -0
  83. package/dist/tests/component.cleanup.test.js +325 -0
  84. package/dist/tests/component.counter.test.d.ts +2 -0
  85. package/dist/tests/component.counter.test.d.ts.map +1 -0
  86. package/dist/tests/component.counter.test.js +124 -0
  87. package/dist/tests/component.interaction.test.d.ts +2 -0
  88. package/dist/tests/component.interaction.test.d.ts.map +1 -0
  89. package/dist/tests/component.interaction.test.js +73 -0
  90. package/dist/tests/component.props.test.d.ts +2 -0
  91. package/dist/tests/component.props.test.d.ts.map +1 -0
  92. package/dist/tests/component.props.test.js +88 -0
  93. package/dist/tests/component.return-types.test.d.ts +2 -0
  94. package/dist/tests/component.return-types.test.d.ts.map +1 -0
  95. package/dist/tests/component.return-types.test.js +357 -0
  96. package/dist/tests/component.state.test.d.ts +2 -0
  97. package/dist/tests/component.state.test.d.ts.map +1 -0
  98. package/dist/tests/component.state.test.js +129 -0
  99. package/dist/tests/component.test.d.ts +2 -0
  100. package/dist/tests/component.test.d.ts.map +1 -0
  101. package/dist/tests/component.test.js +63 -0
  102. package/dist/tests/createAsync.test.d.ts +2 -0
  103. package/dist/tests/createAsync.test.d.ts.map +1 -0
  104. package/dist/tests/createAsync.test.js +110 -0
  105. package/dist/tests/createContext.test.d.ts +2 -0
  106. package/dist/tests/createContext.test.d.ts.map +1 -0
  107. package/dist/tests/createContext.test.js +141 -0
  108. package/dist/tests/createMutation.test.d.ts +2 -0
  109. package/dist/tests/createMutation.test.d.ts.map +1 -0
  110. package/dist/tests/createMutation.test.js +168 -0
  111. package/dist/tests/createQuery.test.d.ts +2 -0
  112. package/dist/tests/createQuery.test.d.ts.map +1 -0
  113. package/dist/tests/createQuery.test.js +156 -0
  114. package/dist/tests/createRef.test.d.ts +2 -0
  115. package/dist/tests/createRef.test.d.ts.map +1 -0
  116. package/dist/tests/createRef.test.js +84 -0
  117. package/dist/tests/createState.test.d.ts +2 -0
  118. package/dist/tests/createState.test.d.ts.map +1 -0
  119. package/dist/tests/createState.test.js +111 -0
  120. package/dist/tests/createView.test.d.ts +2 -0
  121. package/dist/tests/createView.test.d.ts.map +1 -0
  122. package/dist/tests/createView.test.js +203 -0
  123. package/dist/tests/edge-cases.test.d.ts +2 -0
  124. package/dist/tests/edge-cases.test.d.ts.map +1 -0
  125. package/dist/tests/edge-cases.test.js +637 -0
  126. package/dist/tests/error-no-boundary.test.d.ts +2 -0
  127. package/dist/tests/error-no-boundary.test.d.ts.map +1 -0
  128. package/dist/tests/error-no-boundary.test.js +174 -0
  129. package/dist/tests/error.test.d.ts +2 -0
  130. package/dist/tests/error.test.d.ts.map +1 -0
  131. package/dist/tests/error.test.js +199 -0
  132. package/dist/tests/fragment.test.d.ts +2 -0
  133. package/dist/tests/fragment.test.d.ts.map +1 -0
  134. package/dist/tests/fragment.test.js +618 -0
  135. package/dist/tests/integration.test.d.ts +2 -0
  136. package/dist/tests/integration.test.d.ts.map +1 -0
  137. package/dist/tests/integration.test.js +192 -0
  138. package/dist/tests/keys.test.d.ts +2 -0
  139. package/dist/tests/keys.test.d.ts.map +1 -0
  140. package/dist/tests/keys.test.js +293 -0
  141. package/dist/tests/mount.test.d.ts +2 -0
  142. package/dist/tests/mount.test.d.ts.map +1 -0
  143. package/dist/tests/mount.test.js +91 -0
  144. package/dist/tests/observation.test.d.ts +2 -0
  145. package/dist/tests/observation.test.d.ts.map +1 -0
  146. package/dist/tests/observation.test.js +150 -0
  147. package/dist/tests/patch.test.d.ts +2 -0
  148. package/dist/tests/patch.test.d.ts.map +1 -0
  149. package/dist/tests/patch.test.js +498 -0
  150. package/dist/tests/patchChildren.test.d.ts +2 -0
  151. package/dist/tests/patchChildren.test.d.ts.map +1 -0
  152. package/dist/tests/patchChildren.test.js +387 -0
  153. package/dist/tests/primitives.test.d.ts +2 -0
  154. package/dist/tests/primitives.test.d.ts.map +1 -0
  155. package/dist/tests/primitives.test.js +132 -0
  156. package/dist/vdom/AbstractVNode.d.ts +22 -0
  157. package/dist/vdom/AbstractVNode.d.ts.map +1 -0
  158. package/dist/vdom/AbstractVNode.js +106 -0
  159. package/dist/vdom/ComponentVNode.d.ts +48 -0
  160. package/dist/vdom/ComponentVNode.d.ts.map +1 -0
  161. package/dist/vdom/ComponentVNode.js +209 -0
  162. package/dist/vdom/ElementVNode.d.ts +24 -0
  163. package/dist/vdom/ElementVNode.d.ts.map +1 -0
  164. package/dist/vdom/ElementVNode.js +126 -0
  165. package/dist/vdom/FragmentVNode.d.ts +13 -0
  166. package/dist/vdom/FragmentVNode.d.ts.map +1 -0
  167. package/dist/vdom/FragmentVNode.js +34 -0
  168. package/dist/vdom/RootVNode.d.ts +22 -0
  169. package/dist/vdom/RootVNode.d.ts.map +1 -0
  170. package/dist/vdom/RootVNode.js +55 -0
  171. package/dist/vdom/TextVNode.d.ts +11 -0
  172. package/dist/vdom/TextVNode.d.ts.map +1 -0
  173. package/dist/vdom/TextVNode.js +32 -0
  174. package/dist/vdom/class.test.d.ts +2 -0
  175. package/dist/vdom/class.test.d.ts.map +1 -0
  176. package/dist/vdom/class.test.js +143 -0
  177. package/dist/vdom/complex-rendering.test.d.ts +2 -0
  178. package/dist/vdom/complex-rendering.test.d.ts.map +1 -0
  179. package/dist/vdom/complex-rendering.test.js +400 -0
  180. package/dist/vdom/component.cleanup.test.d.ts +2 -0
  181. package/dist/vdom/component.cleanup.test.d.ts.map +1 -0
  182. package/dist/vdom/component.cleanup.test.js +323 -0
  183. package/dist/vdom/component.counter.test.d.ts +2 -0
  184. package/dist/vdom/component.counter.test.d.ts.map +1 -0
  185. package/dist/vdom/component.counter.test.js +124 -0
  186. package/dist/vdom/component.interaction.test.d.ts +2 -0
  187. package/dist/vdom/component.interaction.test.d.ts.map +1 -0
  188. package/dist/vdom/component.interaction.test.js +73 -0
  189. package/dist/vdom/component.props.test.d.ts +2 -0
  190. package/dist/vdom/component.props.test.d.ts.map +1 -0
  191. package/dist/vdom/component.props.test.js +88 -0
  192. package/dist/vdom/component.return-types.test.d.ts +2 -0
  193. package/dist/vdom/component.return-types.test.d.ts.map +1 -0
  194. package/dist/vdom/component.return-types.test.js +357 -0
  195. package/dist/vdom/component.state.test.d.ts +2 -0
  196. package/dist/vdom/component.state.test.d.ts.map +1 -0
  197. package/dist/vdom/component.state.test.js +129 -0
  198. package/dist/vdom/component.test.d.ts +2 -0
  199. package/dist/vdom/component.test.d.ts.map +1 -0
  200. package/dist/vdom/component.test.js +63 -0
  201. package/dist/vdom/dom-utils.d.ts +9 -0
  202. package/dist/vdom/dom-utils.d.ts.map +1 -0
  203. package/dist/vdom/dom-utils.js +74 -0
  204. package/dist/vdom/edge-cases.test.d.ts +2 -0
  205. package/dist/vdom/edge-cases.test.d.ts.map +1 -0
  206. package/dist/vdom/edge-cases.test.js +637 -0
  207. package/dist/vdom/fragment.test.d.ts +2 -0
  208. package/dist/vdom/fragment.test.d.ts.map +1 -0
  209. package/dist/vdom/fragment.test.js +618 -0
  210. package/dist/vdom/index.d.ts +10 -0
  211. package/dist/vdom/index.d.ts.map +1 -0
  212. package/dist/vdom/index.js +26 -0
  213. package/dist/vdom/keys.test.d.ts +2 -0
  214. package/dist/vdom/keys.test.d.ts.map +1 -0
  215. package/dist/vdom/keys.test.js +293 -0
  216. package/dist/vdom/mount.test.d.ts +2 -0
  217. package/dist/vdom/mount.test.d.ts.map +1 -0
  218. package/dist/vdom/mount.test.js +91 -0
  219. package/dist/vdom/patch.test.d.ts +2 -0
  220. package/dist/vdom/patch.test.d.ts.map +1 -0
  221. package/dist/vdom/patch.test.js +498 -0
  222. package/dist/vdom/patchChildren.test.d.ts +2 -0
  223. package/dist/vdom/patchChildren.test.d.ts.map +1 -0
  224. package/dist/vdom/patchChildren.test.js +392 -0
  225. package/dist/vdom/primitives.test.d.ts +2 -0
  226. package/dist/vdom/primitives.test.d.ts.map +1 -0
  227. package/dist/vdom/primitives.test.js +132 -0
  228. package/dist/vdom/types.d.ts +8 -0
  229. package/dist/vdom/types.d.ts.map +1 -0
  230. package/dist/vdom/types.js +1 -0
  231. package/dist/vdom/utils.d.ts +6 -0
  232. package/dist/vdom/utils.d.ts.map +1 -0
  233. package/dist/vdom/utils.js +63 -0
  234. package/package.json +1 -4
@@ -0,0 +1,143 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { jsx, render } from "./index";
3
+ import { createState } from "../createState";
4
+ describe("Class Property Support", () => {
5
+ it("should map string class to className", () => {
6
+ const container = document.createElement("div");
7
+ render(jsx("div", { class: "test-class" }), container);
8
+ const div = container.querySelector("div");
9
+ expect(div?.className).toBe("test-class");
10
+ });
11
+ it("should handle multiple classes as string", () => {
12
+ const container = document.createElement("div");
13
+ render(jsx("div", { class: "class-1 class-2 class-3" }), container);
14
+ const div = container.querySelector("div");
15
+ expect(div?.className).toBe("class-1 class-2 class-3");
16
+ });
17
+ it("should handle object notation with true values", () => {
18
+ const container = document.createElement("div");
19
+ render(jsx("div", {
20
+ class: {
21
+ "active": true,
22
+ "visible": true,
23
+ "hidden": false,
24
+ },
25
+ }), container);
26
+ const div = container.querySelector("div");
27
+ expect(div?.classList.contains("active")).toBe(true);
28
+ expect(div?.classList.contains("visible")).toBe(true);
29
+ expect(div?.classList.contains("hidden")).toBe(false);
30
+ });
31
+ it("should handle mixed object notation", () => {
32
+ const container = document.createElement("div");
33
+ render(jsx("div", {
34
+ class: {
35
+ "class-1": true,
36
+ "class-2": false,
37
+ "class-3": true,
38
+ "class-4": false,
39
+ },
40
+ }), container);
41
+ const div = container.querySelector("div");
42
+ expect(div?.className).toBe("class-1 class-3");
43
+ });
44
+ it("should handle empty object notation", () => {
45
+ const container = document.createElement("div");
46
+ render(jsx("div", { class: {} }), container);
47
+ const div = container.querySelector("div");
48
+ expect(div?.className).toBe("");
49
+ });
50
+ it("should handle all false object notation", () => {
51
+ const container = document.createElement("div");
52
+ render(jsx("div", {
53
+ class: {
54
+ "class-1": false,
55
+ "class-2": false,
56
+ },
57
+ }), container);
58
+ const div = container.querySelector("div");
59
+ expect(div?.className).toBe("");
60
+ });
61
+ it("should update classes when object notation changes", async () => {
62
+ const container = document.createElement("div");
63
+ let stateFn;
64
+ const App = () => {
65
+ const state = createState({ isActive: true, isVisible: false });
66
+ stateFn = state;
67
+ return () => jsx("div", {
68
+ class: {
69
+ active: state.isActive,
70
+ visible: state.isVisible,
71
+ },
72
+ });
73
+ };
74
+ render(jsx(App, {}), container);
75
+ const div = container.querySelector("div");
76
+ expect(div?.classList.contains("active")).toBe(true);
77
+ expect(div?.classList.contains("visible")).toBe(false);
78
+ stateFn.isActive = false;
79
+ stateFn.isVisible = true;
80
+ await new Promise((resolve) => setTimeout(resolve, 0));
81
+ expect(div?.classList.contains("active")).toBe(false);
82
+ expect(div?.classList.contains("visible")).toBe(true);
83
+ });
84
+ it("should handle hyphenated class names in object notation", () => {
85
+ const container = document.createElement("div");
86
+ render(jsx("div", {
87
+ class: {
88
+ "my-custom-class": true,
89
+ "another-class-name": true,
90
+ "disabled-class": false,
91
+ },
92
+ }), container);
93
+ const div = container.querySelector("div");
94
+ expect(div?.classList.contains("my-custom-class")).toBe(true);
95
+ expect(div?.classList.contains("another-class-name")).toBe(true);
96
+ expect(div?.classList.contains("disabled-class")).toBe(false);
97
+ });
98
+ it("should prefer class over className when both provided", () => {
99
+ const container = document.createElement("div");
100
+ render(jsx("div", { class: "from-class", className: "from-className" }), container);
101
+ const div = container.querySelector("div");
102
+ // class should take precedence
103
+ expect(div?.className).toBe("from-class");
104
+ });
105
+ it("should work with nested components", () => {
106
+ const container = document.createElement("div");
107
+ const Child = () => {
108
+ return () => jsx("span", { class: "child-class" });
109
+ };
110
+ const Parent = () => {
111
+ return () => jsx("div", {
112
+ class: "parent-class",
113
+ children: jsx(Child, {}),
114
+ });
115
+ };
116
+ render(jsx(Parent, {}), container);
117
+ const parentDiv = container.querySelector("div");
118
+ const childSpan = container.querySelector("span");
119
+ expect(parentDiv?.className).toBe("parent-class");
120
+ expect(childSpan?.className).toBe("child-class");
121
+ });
122
+ it("should handle undefined and null class values", () => {
123
+ const container = document.createElement("div");
124
+ render(jsx("div", { class: undefined }), container);
125
+ const div = container.querySelector("div");
126
+ expect(div?.className).toBe("");
127
+ });
128
+ it("should handle dynamic string class updates", async () => {
129
+ const container = document.createElement("div");
130
+ let stateFn;
131
+ const App = () => {
132
+ const state = createState({ className: "initial" });
133
+ stateFn = state;
134
+ return () => jsx("div", { class: state.className });
135
+ };
136
+ render(jsx(App, {}), container);
137
+ const div = container.querySelector("div");
138
+ expect(div?.className).toBe("initial");
139
+ stateFn.className = "updated";
140
+ await new Promise((resolve) => setTimeout(resolve, 0));
141
+ expect(div?.className).toBe("updated");
142
+ });
143
+ });
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=complex-rendering.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"complex-rendering.test.d.ts","sourceRoot":"","sources":["../../src/vdom/complex-rendering.test.tsx"],"names":[],"mappings":""}
@@ -0,0 +1,400 @@
1
+ import { describe, it, expect, beforeEach, afterEach, vi } from "vitest";
2
+ import { jsx, render } from "./index";
3
+ import { createState } from "../createState";
4
+ import { Fragment } from "./FragmentVNode";
5
+ describe("Complex Rendering", () => {
6
+ beforeEach(() => {
7
+ vi.useFakeTimers();
8
+ });
9
+ afterEach(() => {
10
+ vi.clearAllTimers();
11
+ });
12
+ it("should handle deeply nested elements, fragments and components", () => {
13
+ const NestedComponent = () => {
14
+ return () => jsx(Fragment, {
15
+ children: [
16
+ jsx("span", { children: "Nested" }),
17
+ jsx("strong", { children: "Component" }),
18
+ ],
19
+ });
20
+ };
21
+ const MiddleComponent = () => {
22
+ return () => jsx("div", {
23
+ class: "middle",
24
+ children: [
25
+ jsx("p", { children: "Fragment in middle" }),
26
+ jsx(NestedComponent, {}),
27
+ jsx("ul", {
28
+ children: [
29
+ jsx("li", { children: "Item 1" }),
30
+ jsx("li", { children: "Item 2" }),
31
+ ],
32
+ }),
33
+ ],
34
+ });
35
+ };
36
+ const App = () => {
37
+ return () => jsx(Fragment, {
38
+ children: [
39
+ jsx("header", {
40
+ children: [
41
+ jsx("h1", { children: "Header" }),
42
+ jsx(MiddleComponent, {}),
43
+ ],
44
+ }),
45
+ jsx("main", {
46
+ children: jsx("section", {
47
+ children: jsx("div", { children: jsx(NestedComponent, {}) }),
48
+ }),
49
+ }),
50
+ ],
51
+ });
52
+ };
53
+ const container = document.createElement("div");
54
+ render(jsx(App, {}), container);
55
+ expect(container.querySelector("header h1")?.textContent).toBe("Header");
56
+ expect(container.querySelector(".middle p")?.textContent).toBe("Fragment in middle");
57
+ expect(container.querySelector(".middle span")?.textContent).toBe("Nested");
58
+ expect(container.querySelector(".middle strong")?.textContent).toBe("Component");
59
+ expect(container.querySelectorAll("li").length).toBe(2);
60
+ expect(container.querySelector("main span")?.textContent).toBe("Nested");
61
+ expect(container.querySelector("main strong")?.textContent).toBe("Component");
62
+ });
63
+ it("should handle multiple fragments and components at same level", () => {
64
+ const Component1 = () => () => jsx("span", { children: "C1" });
65
+ const Component2 = () => () => jsx("span", { children: "C2" });
66
+ const Component3 = () => () => jsx("span", { children: "C3" });
67
+ const App = () => {
68
+ return () => jsx("div", {
69
+ children: [
70
+ jsx(Component1, {}),
71
+ jsx("strong", { children: "Between" }),
72
+ jsx(Component2, {}),
73
+ jsx("em", { children: "Middle" }),
74
+ jsx(Component3, {}),
75
+ jsx("i", { children: "End" }),
76
+ ],
77
+ });
78
+ };
79
+ const container = document.createElement("div");
80
+ render(jsx(App, {}), container);
81
+ const spans = container.querySelectorAll("span");
82
+ expect(spans.length).toBe(3);
83
+ expect(spans[0].textContent).toBe("C1");
84
+ expect(spans[1].textContent).toBe("C2");
85
+ expect(spans[2].textContent).toBe("C3");
86
+ expect(container.querySelector("strong")?.textContent).toBe("Between");
87
+ expect(container.querySelector("em")?.textContent).toBe("Middle");
88
+ expect(container.querySelector("i")?.textContent).toBe("End");
89
+ });
90
+ it("should update multiple nested components when state changes", async () => {
91
+ let globalCount;
92
+ const Counter = () => {
93
+ return () => jsx("span", { class: "counter", children: globalCount.value });
94
+ };
95
+ const DoubleCounter = () => {
96
+ return () => jsx("span", { class: "double", children: globalCount.value * 2 });
97
+ };
98
+ const NestedCounters = () => {
99
+ return () => jsx(Fragment, {
100
+ children: [
101
+ jsx(Counter, {}),
102
+ jsx("div", {
103
+ children: [jsx(DoubleCounter, {}), jsx(Counter, {})],
104
+ }),
105
+ ],
106
+ });
107
+ };
108
+ const App = () => {
109
+ const state = createState({ value: 0 });
110
+ globalCount = state;
111
+ return () => jsx("div", {
112
+ children: [
113
+ jsx(NestedCounters, {}),
114
+ jsx("section", {
115
+ children: [
116
+ jsx(DoubleCounter, {}),
117
+ jsx(Counter, {}),
118
+ jsx(NestedCounters, {}),
119
+ ],
120
+ }),
121
+ ],
122
+ });
123
+ };
124
+ const container = document.createElement("div");
125
+ render(jsx(App, {}), container);
126
+ // Initial state: count = 0
127
+ let counters = container.querySelectorAll(".counter");
128
+ let doubles = container.querySelectorAll(".double");
129
+ expect(counters.length).toBe(5);
130
+ expect(doubles.length).toBe(3);
131
+ counters.forEach((counter) => {
132
+ expect(counter.textContent).toBe("0");
133
+ });
134
+ doubles.forEach((double) => {
135
+ expect(double.textContent).toBe("0");
136
+ });
137
+ // Update state to 5
138
+ globalCount.value = 5;
139
+ await vi.runAllTimersAsync();
140
+ counters = container.querySelectorAll(".counter");
141
+ doubles = container.querySelectorAll(".double");
142
+ counters.forEach((counter) => {
143
+ expect(counter.textContent).toBe("5");
144
+ });
145
+ doubles.forEach((double) => {
146
+ expect(double.textContent).toBe("10");
147
+ });
148
+ // Update state to 10
149
+ globalCount.value = 10;
150
+ await vi.runAllTimersAsync();
151
+ counters = container.querySelectorAll(".counter");
152
+ doubles = container.querySelectorAll(".double");
153
+ counters.forEach((counter) => {
154
+ expect(counter.textContent).toBe("10");
155
+ });
156
+ doubles.forEach((double) => {
157
+ expect(double.textContent).toBe("20");
158
+ });
159
+ });
160
+ it("should handle components that change their return structure based on state", async () => {
161
+ let globalState;
162
+ const DynamicComponent = () => {
163
+ return () => {
164
+ const currentMode = globalState.mode;
165
+ if (currentMode === "list") {
166
+ return jsx("ul", {
167
+ class: "list",
168
+ children: [
169
+ jsx("li", { children: "Item 1" }),
170
+ jsx("li", { children: "Item 2" }),
171
+ jsx("li", { children: "Item 3" }),
172
+ ],
173
+ });
174
+ }
175
+ if (currentMode === "grid") {
176
+ return jsx("div", {
177
+ class: "grid",
178
+ children: [
179
+ jsx("div", { class: "card", children: "Card 1" }),
180
+ jsx("div", { class: "card", children: "Card 2" }),
181
+ jsx("div", { class: "card", children: "Card 3" }),
182
+ ],
183
+ });
184
+ }
185
+ return jsx("table", {
186
+ class: "table",
187
+ children: jsx("tbody", {
188
+ children: jsx("tr", {
189
+ children: [
190
+ jsx("td", { children: "Cell 1" }),
191
+ jsx("td", { children: "Cell 2" }),
192
+ jsx("td", { children: "Cell 3" }),
193
+ ],
194
+ }),
195
+ }),
196
+ });
197
+ };
198
+ };
199
+ const NestedDynamic = () => {
200
+ return () => jsx(Fragment, {
201
+ children: [
202
+ jsx("h2", { children: `Mode: ${globalState.mode}` }),
203
+ jsx(DynamicComponent, {}),
204
+ ],
205
+ });
206
+ };
207
+ const App = () => {
208
+ const state = createState({
209
+ mode: "list",
210
+ });
211
+ globalState = state;
212
+ return () => jsx("div", {
213
+ children: [
214
+ jsx(NestedDynamic, {}),
215
+ jsx("footer", {
216
+ children: jsx(DynamicComponent, {}),
217
+ }),
218
+ ],
219
+ });
220
+ };
221
+ const container = document.createElement("div");
222
+ render(jsx(App, {}), container);
223
+ // Initial state: list mode
224
+ expect(container.querySelector("h2")?.textContent).toBe("Mode: list");
225
+ expect(container.querySelectorAll(".list").length).toBe(2);
226
+ expect(container.querySelectorAll("li").length).toBe(6);
227
+ expect(container.querySelector(".grid")).toBeNull();
228
+ expect(container.querySelector(".table")).toBeNull();
229
+ // Switch to grid mode
230
+ globalState.mode = "grid";
231
+ await vi.runAllTimersAsync();
232
+ expect(container.querySelector("h2")?.textContent).toBe("Mode: grid");
233
+ expect(container.querySelectorAll(".grid").length).toBe(2);
234
+ expect(container.querySelectorAll(".card").length).toBe(6);
235
+ expect(container.querySelector(".list")).toBeNull();
236
+ expect(container.querySelector(".table")).toBeNull();
237
+ // Switch to table mode
238
+ globalState.mode = "table";
239
+ await vi.runAllTimersAsync();
240
+ return;
241
+ expect(container.querySelector("h2")?.textContent).toBe("Mode: table");
242
+ expect(container.querySelectorAll(".table").length).toBe(2);
243
+ expect(container.querySelectorAll("td").length).toBe(6);
244
+ expect(container.querySelector(".list")).toBeNull();
245
+ expect(container.querySelector(".grid")).toBeNull();
246
+ // Switch back to list
247
+ globalState.mode = "list";
248
+ await vi.runAllTimersAsync();
249
+ expect(container.querySelector("h2")?.textContent).toBe("Mode: list");
250
+ expect(container.querySelectorAll(".list").length).toBe(2);
251
+ expect(container.querySelectorAll("li").length).toBe(6);
252
+ });
253
+ it("should handle deeply nested state changes with fragments", async () => {
254
+ let globalUser;
255
+ const UserName = () => {
256
+ return () => jsx("span", { class: "name", children: globalUser.name });
257
+ };
258
+ const UserAge = () => {
259
+ return () => jsx("span", { class: "age", children: globalUser.age });
260
+ };
261
+ const UserRole = () => {
262
+ return () => jsx("span", { class: "role", children: globalUser.role });
263
+ };
264
+ const UserInfo = () => {
265
+ return () => jsx(Fragment, {
266
+ children: [
267
+ jsx(UserName, {}),
268
+ jsx("div", {
269
+ class: "details",
270
+ children: [jsx(UserAge, {}), jsx(UserRole, {})],
271
+ }),
272
+ ],
273
+ });
274
+ };
275
+ const UserCard = () => {
276
+ return () => jsx("div", {
277
+ class: "card",
278
+ children: [
279
+ jsx("h3", { children: "User Profile" }),
280
+ jsx(UserInfo, {}),
281
+ jsx("footer", { children: ["Role: ", jsx(UserRole, {})] }),
282
+ ],
283
+ });
284
+ };
285
+ const App = () => {
286
+ const state = createState({ name: "John", age: 30, role: "admin" });
287
+ globalUser = state;
288
+ return () => jsx(Fragment, {
289
+ children: [
290
+ jsx(UserCard, {}),
291
+ jsx("aside", {
292
+ children: [
293
+ jsx(UserName, {}),
294
+ " is ",
295
+ jsx(UserAge, {}),
296
+ " years old",
297
+ ],
298
+ }),
299
+ ],
300
+ });
301
+ };
302
+ const container = document.createElement("div");
303
+ render(jsx(App, {}), container);
304
+ expect(container.querySelector(".name")?.textContent).toBe("John");
305
+ expect(container.querySelectorAll(".age")[0].textContent).toBe("30");
306
+ expect(container.querySelectorAll(".role").length).toBe(2);
307
+ expect(container.querySelector("aside")?.textContent).toBe("John is 30 years old");
308
+ // Update user
309
+ globalUser.name = "Jane";
310
+ globalUser.age = 25;
311
+ globalUser.role = "user";
312
+ await vi.runAllTimersAsync();
313
+ expect(container.querySelector(".name")?.textContent).toBe("Jane");
314
+ expect(container.querySelectorAll(".age")[0].textContent).toBe("25");
315
+ const roles = container.querySelectorAll(".role");
316
+ roles.forEach((role) => {
317
+ expect(role.textContent).toBe("user");
318
+ });
319
+ expect(container.querySelector("aside")?.textContent).toBe("Jane is 25 years old");
320
+ // Update user again
321
+ globalUser.name = "Bob";
322
+ globalUser.age = 40;
323
+ globalUser.role = "moderator";
324
+ await vi.runAllTimersAsync();
325
+ expect(container.querySelector(".name")?.textContent).toBe("Bob");
326
+ expect(container.querySelectorAll(".age")[0].textContent).toBe("40");
327
+ const newRoles = container.querySelectorAll(".role");
328
+ newRoles.forEach((role) => {
329
+ expect(role.textContent).toBe("moderator");
330
+ });
331
+ expect(container.querySelector("aside")?.textContent).toBe("Bob is 40 years old");
332
+ });
333
+ it("should handle mixed nested updates with conditional rendering", async () => {
334
+ let globalState;
335
+ const Details = () => {
336
+ return () => jsx(Fragment, {
337
+ children: [
338
+ jsx("p", { children: `Count: ${globalState.count}` }),
339
+ jsx("p", { children: `Double: ${globalState.count * 2}` }),
340
+ ],
341
+ });
342
+ };
343
+ const Card = () => {
344
+ return () => jsx("div", {
345
+ class: "card",
346
+ children: [
347
+ jsx("h4", { children: `Card ${globalState.count}` }),
348
+ globalState.showDetails
349
+ ? jsx(Details, {})
350
+ : jsx("p", { children: "No details" }),
351
+ ],
352
+ });
353
+ };
354
+ const App = () => {
355
+ const state = createState({ showDetails: false, count: 0 });
356
+ globalState = state;
357
+ return () => jsx(Fragment, {
358
+ children: [
359
+ jsx(Card, {}),
360
+ jsx("div", {
361
+ children: [
362
+ jsx(Card, {}),
363
+ jsx("span", { children: `Total: ${state.count}` }),
364
+ ],
365
+ }),
366
+ ],
367
+ });
368
+ };
369
+ const container = document.createElement("div");
370
+ render(jsx(App, {}), container);
371
+ // Initial state
372
+ expect(container.querySelectorAll(".card h4")[0].textContent).toBe("Card 0");
373
+ expect(container.querySelectorAll(".card h4")[1].textContent).toBe("Card 0");
374
+ expect(container.querySelectorAll("p").length).toBe(2);
375
+ expect(container.querySelectorAll("p")[0].textContent).toBe("No details");
376
+ expect(container.querySelector("span")?.textContent).toBe("Total: 0");
377
+ // Show details
378
+ globalState.showDetails = true;
379
+ await vi.runAllTimersAsync();
380
+ expect(container.querySelectorAll("p").length).toBe(4);
381
+ expect(container.querySelectorAll("p")[0].textContent).toBe("Count: 0");
382
+ expect(container.querySelectorAll("p")[1].textContent).toBe("Double: 0");
383
+ // Increment count
384
+ globalState.count = 5;
385
+ await vi.runAllTimersAsync();
386
+ expect(container.querySelectorAll(".card h4")[0].textContent).toBe("Card 5");
387
+ expect(container.querySelectorAll(".card h4")[1].textContent).toBe("Card 5");
388
+ expect(container.querySelectorAll("p")[0].textContent).toBe("Count: 5");
389
+ expect(container.querySelectorAll("p")[1].textContent).toBe("Double: 10");
390
+ expect(container.querySelector("span")?.textContent).toBe("Total: 5");
391
+ // Hide details and increment
392
+ globalState.showDetails = false;
393
+ globalState.count = 10;
394
+ await vi.runAllTimersAsync();
395
+ expect(container.querySelectorAll(".card h4")[0].textContent).toBe("Card 10");
396
+ expect(container.querySelectorAll("p").length).toBe(2);
397
+ expect(container.querySelectorAll("p")[0].textContent).toBe("No details");
398
+ expect(container.querySelector("span")?.textContent).toBe("Total: 10");
399
+ });
400
+ });
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=component.cleanup.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"component.cleanup.test.d.ts","sourceRoot":"","sources":["../../src/vdom/component.cleanup.test.tsx"],"names":[],"mappings":""}