lithent 0.36.2 → 1.0.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/README.md +51 -1
- package/dist/hook/mountCallback.d.ts +3 -1
- package/dist/hook/ref.d.ts +1 -0
- package/dist/index.d.ts +2 -2
- package/dist/lithent.mjs +67 -44
- package/dist/lithent.mjs.map +1 -1
- package/dist/lithent.umd.js +1 -1
- package/dist/lithent.umd.js.map +1 -1
- package/dist/types/index.d.ts +3 -2
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/predicator.d.ts +2 -1
- package/dist/utils/universalRef.d.ts +3 -9
- package/dist/wDom.d.ts +0 -1
- package/examples/dist/assets/index.2b484d3b.js +2 -0
- package/examples/dist/assets/index.2b484d3b.js.map +1 -0
- package/examples/dist/index.html +1 -1
- package/package.json +11 -5
- package/src/diff.ts +5 -12
- package/src/hook/mountCallback.ts +11 -4
- package/src/hook/ref.ts +1 -0
- package/src/hook/updateCallback.ts +1 -9
- package/src/index.ts +11 -2
- package/src/render.ts +48 -31
- package/src/{examples → tests}/callback.tsx +0 -1
- package/src/tests/example1.tsx +75 -0
- package/src/tests/example10.tsx +90 -0
- package/src/tests/example11.tsx +82 -0
- package/src/tests/example12.tsx +76 -0
- package/src/tests/example13.tsx +59 -0
- package/src/tests/example14.tsx +115 -0
- package/src/tests/example15.tsx +68 -0
- package/src/tests/example16.tsx +49 -0
- package/src/tests/example17.tsx +50 -0
- package/src/tests/example2.tsx +104 -0
- package/src/tests/example3.tsx +96 -0
- package/src/tests/example4.tsx +138 -0
- package/src/tests/example5.tsx +63 -0
- package/src/tests/example6.tsx +54 -0
- package/src/tests/example7.tsx +21 -0
- package/src/tests/example8.tsx +68 -0
- package/src/tests/example9.tsx +46 -0
- package/src/tests/renew.tsx +56 -0
- package/src/tests/svg.tsx +44 -0
- package/src/types/index.ts +12 -13
- package/src/utils/index.ts +3 -0
- package/src/utils/predicator.ts +6 -2
- package/src/utils/universalRef.ts +15 -9
- package/src/wDom.ts +5 -7
- package/examples/dist/assets/index.f15c0062.js +0 -2
- package/examples/dist/assets/index.f15c0062.js.map +0 -1
- package/src/examples/renew.tsx +0 -29
- /package/src/{examples → tests}/insert.tsx +0 -0
- /package/src/{examples → tests}/loop.tsx +0 -0
- /package/src/{examples → tests}/nested.tsx +0 -0
- /package/src/{examples → tests}/selectbox.tsx +0 -0
- /package/src/{examples → tests}/textarea.tsx +0 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lithent",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "1.0.0",
|
|
4
4
|
"description": "An extensible virtual DOM library for lightweight use in a variety of environments.",
|
|
5
5
|
"packageManager": "pnpm@7.6.0",
|
|
6
6
|
"main": "./dist/lithent.umd.js",
|
|
@@ -17,8 +17,8 @@
|
|
|
17
17
|
"build": "pnpm build:core && pnpm build:helper && pnpm build:examples && pnpm build:docs",
|
|
18
18
|
"build:p": "pnpm build:core && pnpm -r build",
|
|
19
19
|
"dev": "pnpm watch:core & pnpm watch:helper & pnpm dev:examples",
|
|
20
|
-
"test": "
|
|
21
|
-
"
|
|
20
|
+
"test": "vitest",
|
|
21
|
+
"coverage": "vitest run --coverage",
|
|
22
22
|
"clean": "pnpm clean:dist && pnpm clean:module",
|
|
23
23
|
"clean:dist": "pnpm -r exec rm -rf dist && rm -rf dist",
|
|
24
24
|
"clean:module": "pnpm -r exec rm -rf dist node_modules && rm -rf node_modules",
|
|
@@ -58,7 +58,9 @@
|
|
|
58
58
|
},
|
|
59
59
|
"homepage": "https://github.com/superlucky84/lithent",
|
|
60
60
|
"license": "MIT",
|
|
61
|
-
"keywords": [
|
|
61
|
+
"keywords": [
|
|
62
|
+
"lithent"
|
|
63
|
+
],
|
|
62
64
|
"devDependencies": {
|
|
63
65
|
"@babel/cli": "^7.18.9",
|
|
64
66
|
"@babel/core": "^7.18.9",
|
|
@@ -67,16 +69,20 @@
|
|
|
67
69
|
"@nabla/vite-plugin-eslint": "^1.4.1",
|
|
68
70
|
"@typescript-eslint/eslint-plugin": "^5.30.7",
|
|
69
71
|
"@typescript-eslint/parser": "^5.30.7",
|
|
72
|
+
"@vitest/coverage-v8": "^0.32.2",
|
|
70
73
|
"babel-plugin-module-resolver": "^4.1.0",
|
|
71
74
|
"eslint": "^7.32.0",
|
|
72
75
|
"eslint-config-prettier": "^8.3.0",
|
|
73
76
|
"eslint-plugin-prettier": "^4.0.0",
|
|
77
|
+
"happy-dom": "^9.20.3",
|
|
78
|
+
"jsdom": "^22.1.0",
|
|
74
79
|
"prettier": "^2.3.2",
|
|
75
80
|
"terser": "^5.17.7",
|
|
76
81
|
"typescript": "^4.7.4",
|
|
77
82
|
"vite": "3.2.7",
|
|
78
83
|
"vite-plugin-checker": "^0.4.9",
|
|
79
|
-
"vite-plugin-dts": "^2.3.0"
|
|
84
|
+
"vite-plugin-dts": "^2.3.0",
|
|
85
|
+
"vitest": "^0.32.2"
|
|
80
86
|
},
|
|
81
87
|
"volta": {
|
|
82
88
|
"node": "20.3.0"
|
package/src/diff.ts
CHANGED
|
@@ -146,7 +146,7 @@ const remakeChildrenForUpdate = (newWDom: WDom, originalWDom: WDom) => {
|
|
|
146
146
|
};
|
|
147
147
|
|
|
148
148
|
const remakeChildrenForLoopUpdate = (newWDom: WDom, originalWDom: WDom) => {
|
|
149
|
-
const
|
|
149
|
+
const [remakedChildren, unUsedChildren] = diffLoopChildren(
|
|
150
150
|
newWDom,
|
|
151
151
|
originalWDom
|
|
152
152
|
);
|
|
@@ -165,7 +165,6 @@ const remakeChildrenForLoopUpdate = (newWDom: WDom, originalWDom: WDom) => {
|
|
|
165
165
|
const diffLoopChildren = (newWDom: WDom, originalWDom: WDom) => {
|
|
166
166
|
const newChildren = [...(newWDom.children || [])];
|
|
167
167
|
const originalChildren = [...(originalWDom.children || [])];
|
|
168
|
-
|
|
169
168
|
const remakedChildren = newChildren.map(item => {
|
|
170
169
|
const originalItem = findSameKeyOriginalItem(item, originalChildren);
|
|
171
170
|
const childItem = makeNewWDomTree(item, originalItem);
|
|
@@ -179,19 +178,13 @@ const diffLoopChildren = (newWDom: WDom, originalWDom: WDom) => {
|
|
|
179
178
|
return childItem;
|
|
180
179
|
});
|
|
181
180
|
|
|
182
|
-
return
|
|
183
|
-
remakedChildren,
|
|
184
|
-
unUsedChildren: originalChildren,
|
|
185
|
-
};
|
|
181
|
+
return [remakedChildren, originalChildren];
|
|
186
182
|
};
|
|
187
183
|
|
|
188
|
-
const findSameKeyOriginalItem = (item: WDom, originalChildren: WDom[]) =>
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
return originalChildren.find(
|
|
192
|
-
orignalChildItem => getKey(orignalChildItem) === key
|
|
184
|
+
const findSameKeyOriginalItem = (item: WDom, originalChildren: WDom[]) =>
|
|
185
|
+
originalChildren.find(
|
|
186
|
+
orignalChildItem => getKey(orignalChildItem) === getKey(item)
|
|
193
187
|
);
|
|
194
|
-
};
|
|
195
188
|
|
|
196
189
|
const getKey = (target: WDom) =>
|
|
197
190
|
target?.componentProps?.key ?? target?.props?.key;
|
|
@@ -4,14 +4,21 @@ import {
|
|
|
4
4
|
componentRef,
|
|
5
5
|
getComponentKey,
|
|
6
6
|
} from '@/utils/universalRef';
|
|
7
|
-
|
|
8
7
|
import { unmount } from '@/hook/unmount';
|
|
9
8
|
|
|
10
|
-
|
|
11
|
-
const mts = componentRef.get(getComponentKey())!.mts;
|
|
9
|
+
let mountedQueue: WDom[] = [];
|
|
12
10
|
|
|
13
|
-
|
|
11
|
+
export const execMountedQueue = () => {
|
|
12
|
+
mountedQueue.forEach(item => runMountedQueueFromWDom(item));
|
|
13
|
+
mountedQueue = [];
|
|
14
|
+
};
|
|
15
|
+
export const addMountedQueue = (wDom: WDom) => {
|
|
16
|
+
if (wDom.componentKey) {
|
|
17
|
+
mountedQueue.push(wDom);
|
|
18
|
+
}
|
|
14
19
|
};
|
|
20
|
+
export const mountCallback = (effectAction: () => void) =>
|
|
21
|
+
componentRef.get(getComponentKey())!.mts.push(effectAction);
|
|
15
22
|
|
|
16
23
|
export const runMountedQueueFromWDom = (newWDom: WDom) => {
|
|
17
24
|
const { componentKey } = newWDom;
|
package/src/hook/ref.ts
CHANGED
|
@@ -1,9 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
componentRef,
|
|
3
|
-
redrawQueue,
|
|
4
|
-
redrawQueueTimeout,
|
|
5
|
-
getComponentKey,
|
|
6
|
-
} from '@/utils/universalRef';
|
|
1
|
+
import { componentRef, getComponentKey } from '@/utils/universalRef';
|
|
7
2
|
|
|
8
3
|
import { useUpdated } from '@/hook/useUpdate';
|
|
9
4
|
|
|
@@ -23,7 +18,4 @@ export const runUpdateCallback = () => {
|
|
|
23
18
|
if (updateReqs?.length) {
|
|
24
19
|
updateReqs.forEach(callback => callback());
|
|
25
20
|
}
|
|
26
|
-
|
|
27
|
-
redrawQueue.value = [];
|
|
28
|
-
redrawQueueTimeout.value = null;
|
|
29
21
|
};
|
package/src/index.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { h, Fragment, mount } from '@/wDom';
|
|
|
2
2
|
import { render } from '@/render';
|
|
3
3
|
import { mountCallback } from '@/hook/mountCallback';
|
|
4
4
|
import { updateCallback } from '@/hook/updateCallback';
|
|
5
|
-
import { ref } from '@/hook/ref';
|
|
5
|
+
import { ref, nextTick } from '@/hook/ref';
|
|
6
6
|
|
|
7
7
|
export type {
|
|
8
8
|
WDom,
|
|
@@ -27,4 +27,13 @@ declare global {
|
|
|
27
27
|
}
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
-
export {
|
|
30
|
+
export {
|
|
31
|
+
h,
|
|
32
|
+
Fragment,
|
|
33
|
+
render,
|
|
34
|
+
mount,
|
|
35
|
+
mountCallback,
|
|
36
|
+
updateCallback,
|
|
37
|
+
ref,
|
|
38
|
+
nextTick,
|
|
39
|
+
};
|
package/src/render.ts
CHANGED
|
@@ -7,13 +7,14 @@ import {
|
|
|
7
7
|
checkOptionElement,
|
|
8
8
|
checkTextareaElement,
|
|
9
9
|
checkCheckableElement,
|
|
10
|
+
checkVirtualType,
|
|
10
11
|
} from '@/utils/predicator';
|
|
11
12
|
|
|
12
|
-
import { componentRef } from '@/utils/universalRef';
|
|
13
|
+
import { componentRef, xmlnsRef } from '@/utils/universalRef';
|
|
13
14
|
import { runUnmountQueueFromWDom } from '@/hook/unmount';
|
|
14
|
-
import {
|
|
15
|
+
import { execMountedQueue, addMountedQueue } from '@/hook/mountCallback';
|
|
15
16
|
import { runUpdatedQueueFromWDom } from '@/hook/useUpdate';
|
|
16
|
-
import { getParent, getEventName } from '@/utils';
|
|
17
|
+
import { getParent, getEventName, getAttrKey } from '@/utils';
|
|
17
18
|
|
|
18
19
|
export const render = (
|
|
19
20
|
wDom: WDom,
|
|
@@ -33,14 +34,14 @@ export const render = (
|
|
|
33
34
|
wrapElement.appendChild(Dom);
|
|
34
35
|
}
|
|
35
36
|
|
|
37
|
+
execMountedQueue();
|
|
38
|
+
|
|
36
39
|
return () => {
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
rootDelete(component);
|
|
43
|
-
}
|
|
40
|
+
const component = componentRef.get(wDom.componentProps || {})?.vd.value;
|
|
41
|
+
if (component) {
|
|
42
|
+
runUnmountQueueFromWDom(component);
|
|
43
|
+
recursiveRemoveEvent(component);
|
|
44
|
+
rootDelete(component);
|
|
44
45
|
}
|
|
45
46
|
};
|
|
46
47
|
};
|
|
@@ -121,7 +122,7 @@ const typeAdd = (
|
|
|
121
122
|
newElement?: HTMLElement | DocumentFragment | Text
|
|
122
123
|
) => {
|
|
123
124
|
if (!newElement) {
|
|
124
|
-
newElement = wDomToDom(newWDom);
|
|
125
|
+
newElement = wDomToDom(newWDom) as HTMLElement;
|
|
125
126
|
}
|
|
126
127
|
|
|
127
128
|
const parentWDom = getParent(newWDom);
|
|
@@ -138,10 +139,10 @@ const typeAdd = (
|
|
|
138
139
|
} else {
|
|
139
140
|
parentEl.appendChild(newElement);
|
|
140
141
|
}
|
|
142
|
+
|
|
143
|
+
execMountedQueue();
|
|
141
144
|
}
|
|
142
145
|
}
|
|
143
|
-
|
|
144
|
-
runMountedQueueFromWDom(newWDom);
|
|
145
146
|
};
|
|
146
147
|
|
|
147
148
|
const startFindNextBrotherElement = (
|
|
@@ -160,7 +161,7 @@ const startFindNextBrotherElement = (
|
|
|
160
161
|
return finedNextEl;
|
|
161
162
|
}
|
|
162
163
|
|
|
163
|
-
if (!parentWDom.isRoot &&
|
|
164
|
+
if (!parentWDom.isRoot && checkVirtualType(parentType)) {
|
|
164
165
|
return startFindNextBrotherElement(parentWDom, getParent(parentWDom));
|
|
165
166
|
} else if (parentWDom.isRoot && parentWDom.afterElement) {
|
|
166
167
|
return parentWDom.afterElement;
|
|
@@ -179,7 +180,7 @@ const findChildFragmentNextElement = (
|
|
|
179
180
|
) => {
|
|
180
181
|
const type = bItem.type;
|
|
181
182
|
const el = bItem.el;
|
|
182
|
-
const isFragment = type &&
|
|
183
|
+
const isFragment = type && checkVirtualType(type);
|
|
183
184
|
|
|
184
185
|
if (targetEl) {
|
|
185
186
|
return targetEl;
|
|
@@ -208,10 +209,9 @@ const typeReplace = (newWDom: WDom) => {
|
|
|
208
209
|
if (parentElement) {
|
|
209
210
|
parentElement.replaceChild(newElement, orignalElement);
|
|
210
211
|
}
|
|
212
|
+
execMountedQueue();
|
|
211
213
|
}
|
|
212
214
|
}
|
|
213
|
-
|
|
214
|
-
runMountedQueueFromWDom(newWDom);
|
|
215
215
|
};
|
|
216
216
|
|
|
217
217
|
const removeEvent = (
|
|
@@ -266,7 +266,7 @@ const updateText = (newWDom: WDom) => {
|
|
|
266
266
|
|
|
267
267
|
const updateProps = (
|
|
268
268
|
props?: Props,
|
|
269
|
-
element?: HTMLElement | DocumentFragment | Text,
|
|
269
|
+
element?: HTMLElement | Element | DocumentFragment | Text,
|
|
270
270
|
oldProps?: Props
|
|
271
271
|
) => {
|
|
272
272
|
const originalProps = { ...oldProps };
|
|
@@ -275,11 +275,7 @@ const updateProps = (
|
|
|
275
275
|
([dataKey, dataValue]: [string, unknown]) => {
|
|
276
276
|
if (dataKey === 'key') {
|
|
277
277
|
// Do nothing
|
|
278
|
-
} else if (
|
|
279
|
-
dataKey === 'innerHTML' &&
|
|
280
|
-
element &&
|
|
281
|
-
typeof dataValue === 'string'
|
|
282
|
-
) {
|
|
278
|
+
} else if (dataKey === 'innerHTML' && typeof dataValue === 'string') {
|
|
283
279
|
(element as HTMLElement).innerHTML = dataValue;
|
|
284
280
|
} else if (checkStyleData(dataKey, dataValue)) {
|
|
285
281
|
updateStyle(
|
|
@@ -305,7 +301,18 @@ const updateProps = (
|
|
|
305
301
|
} else if (checkOptionElement(element) && dataKey === 'selected') {
|
|
306
302
|
(element as HTMLOptionElement).selected = !!dataValue;
|
|
307
303
|
} else if (checkNormalAttribute(dataValue)) {
|
|
308
|
-
(
|
|
304
|
+
if (xmlnsRef.value && dataKey !== 'xmlns') {
|
|
305
|
+
(element as HTMLElement).setAttributeNS(
|
|
306
|
+
null,
|
|
307
|
+
getAttrKey(dataKey),
|
|
308
|
+
String(dataValue)
|
|
309
|
+
);
|
|
310
|
+
} else {
|
|
311
|
+
(element as HTMLElement).setAttribute(
|
|
312
|
+
getAttrKey(dataKey),
|
|
313
|
+
String(dataValue)
|
|
314
|
+
);
|
|
315
|
+
}
|
|
309
316
|
}
|
|
310
317
|
|
|
311
318
|
delete originalProps[dataKey];
|
|
@@ -320,12 +327,18 @@ const updateProps = (
|
|
|
320
327
|
const wDomToDom = (wDom: WDom) => {
|
|
321
328
|
let element;
|
|
322
329
|
const { type, tag, text, props, children = [] } = wDom;
|
|
323
|
-
const isVirtualType = type
|
|
330
|
+
const isVirtualType = checkVirtualType(type);
|
|
331
|
+
|
|
332
|
+
if (tag === 'svg') {
|
|
333
|
+
xmlnsRef.value = String(props?.xmlns);
|
|
334
|
+
}
|
|
324
335
|
|
|
325
336
|
if (isVirtualType) {
|
|
326
337
|
element = new DocumentFragment();
|
|
327
338
|
} else if (type === 'element' && tag) {
|
|
328
|
-
element =
|
|
339
|
+
element = xmlnsRef.value
|
|
340
|
+
? document.createElementNS(xmlnsRef.value, tag)
|
|
341
|
+
: document.createElement(tag);
|
|
329
342
|
} else if (type === 'text' && checkExisty(text)) {
|
|
330
343
|
element = document.createTextNode(String(text));
|
|
331
344
|
} else {
|
|
@@ -335,16 +348,20 @@ const wDomToDom = (wDom: WDom) => {
|
|
|
335
348
|
wDomChildrenToDom(children, element);
|
|
336
349
|
updateProps(props, element);
|
|
337
350
|
|
|
338
|
-
wDom.el = element;
|
|
351
|
+
wDom.el = element as HTMLElement;
|
|
339
352
|
|
|
340
|
-
|
|
353
|
+
addMountedQueue(wDom);
|
|
354
|
+
|
|
355
|
+
if (tag === 'svg') {
|
|
356
|
+
xmlnsRef.value = '';
|
|
357
|
+
}
|
|
341
358
|
|
|
342
359
|
return element;
|
|
343
360
|
};
|
|
344
361
|
|
|
345
362
|
const wDomChildrenToDom = (
|
|
346
363
|
children: WDom[],
|
|
347
|
-
parentElement?: HTMLElement | DocumentFragment | Text
|
|
364
|
+
parentElement?: HTMLElement | Element | DocumentFragment | Text
|
|
348
365
|
) => {
|
|
349
366
|
const elementChildren = children.reduce(
|
|
350
367
|
(acc: DocumentFragment, childItem: WDom) => {
|
|
@@ -384,7 +401,7 @@ const updateEvent = (
|
|
|
384
401
|
const updateStyle = (
|
|
385
402
|
style: Record<string, string>,
|
|
386
403
|
oldStyle: Record<string, string>,
|
|
387
|
-
element?: HTMLElement | DocumentFragment | Text
|
|
404
|
+
element?: HTMLElement | Element | DocumentFragment | Text
|
|
388
405
|
) => {
|
|
389
406
|
const originalStyle = { ...oldStyle };
|
|
390
407
|
const elementStyle = (element as HTMLElement)?.style;
|
|
@@ -404,7 +421,7 @@ const updateStyle = (
|
|
|
404
421
|
const findRealParentElement = (
|
|
405
422
|
vDom: WDom
|
|
406
423
|
): HTMLElement | DocumentFragment | Text | undefined => {
|
|
407
|
-
const isVirtualType = vDom.type
|
|
424
|
+
const isVirtualType = checkVirtualType(vDom.type);
|
|
408
425
|
if (vDom.isRoot && isVirtualType) {
|
|
409
426
|
return vDom.wrapElement;
|
|
410
427
|
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
// example.jsx
|
|
2
|
+
import { h, Fragment, render, mount, ref, nextTick } from '@/index';
|
|
3
|
+
const testChangeRef = ref<null | (() => void)>(null);
|
|
4
|
+
|
|
5
|
+
const computed = <T,>(
|
|
6
|
+
value: () => T
|
|
7
|
+
): {
|
|
8
|
+
value: T;
|
|
9
|
+
v: T;
|
|
10
|
+
} => {
|
|
11
|
+
let result = value;
|
|
12
|
+
|
|
13
|
+
return {
|
|
14
|
+
get value() {
|
|
15
|
+
return result();
|
|
16
|
+
},
|
|
17
|
+
get v() {
|
|
18
|
+
return result();
|
|
19
|
+
},
|
|
20
|
+
set value(_newValue: T) {
|
|
21
|
+
throw new Error(`You can't change 'computed'`);
|
|
22
|
+
},
|
|
23
|
+
set v(_newValue: T) {
|
|
24
|
+
throw new Error(`You can't change 'computed'`);
|
|
25
|
+
},
|
|
26
|
+
};
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
const Component = mount(renew => {
|
|
30
|
+
let count = 1;
|
|
31
|
+
const increase = () => {
|
|
32
|
+
count += 1;
|
|
33
|
+
renew();
|
|
34
|
+
};
|
|
35
|
+
testChangeRef.value = increase;
|
|
36
|
+
const result = computed<number>(() =>
|
|
37
|
+
[1, 3, 5, 7, 9].reduce(
|
|
38
|
+
(accumulator, current) => accumulator + current * count,
|
|
39
|
+
0
|
|
40
|
+
)
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
return () => (
|
|
44
|
+
<>
|
|
45
|
+
<button type="text" onClick={increase}>
|
|
46
|
+
increase
|
|
47
|
+
</button>
|
|
48
|
+
<span>computed: {result.v}</span>
|
|
49
|
+
</>
|
|
50
|
+
);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
const testWrap =
|
|
54
|
+
document.getElementById('root') || document.createElement('div');
|
|
55
|
+
|
|
56
|
+
render(<Component />, testWrap);
|
|
57
|
+
|
|
58
|
+
if (import.meta.vitest) {
|
|
59
|
+
const { it, expect } = import.meta.vitest;
|
|
60
|
+
it('Is renew working properly?', () => {
|
|
61
|
+
expect(testWrap.outerHTML).toBe(
|
|
62
|
+
'<div><button type="text">increase</button><span>computed: 25</span></div>'
|
|
63
|
+
);
|
|
64
|
+
if (testChangeRef.value) {
|
|
65
|
+
testChangeRef.value();
|
|
66
|
+
testChangeRef.value();
|
|
67
|
+
testChangeRef.value();
|
|
68
|
+
}
|
|
69
|
+
nextTick().then(() => {
|
|
70
|
+
expect(testWrap.outerHTML).toBe(
|
|
71
|
+
'<div><button type="text">increase</button><span>computed: 100</span></div>'
|
|
72
|
+
);
|
|
73
|
+
});
|
|
74
|
+
});
|
|
75
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
// example.jsx
|
|
2
|
+
import { h, Fragment, render, mount, ref, nextTick } from '@/index';
|
|
3
|
+
const testChangeRef = ref<null | ((newList: string[]) => void)>(null);
|
|
4
|
+
|
|
5
|
+
const Checkbox = mount(r => {
|
|
6
|
+
let checkedList = ['sara'];
|
|
7
|
+
|
|
8
|
+
const handleInput = (event: InputEvent) => {
|
|
9
|
+
const value = (event.target as HTMLInputElement).value;
|
|
10
|
+
const isInclude = checkedList.includes(value);
|
|
11
|
+
|
|
12
|
+
if (isInclude) {
|
|
13
|
+
checkedList = [...checkedList.filter(item => item !== value)];
|
|
14
|
+
} else {
|
|
15
|
+
checkedList = [...checkedList, value];
|
|
16
|
+
}
|
|
17
|
+
r();
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
testChangeRef.value = (newList: string[]) => {
|
|
21
|
+
checkedList = newList;
|
|
22
|
+
r();
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
return () => (
|
|
26
|
+
<>
|
|
27
|
+
<input
|
|
28
|
+
type="checkbox"
|
|
29
|
+
name="checkname"
|
|
30
|
+
onChange={handleInput}
|
|
31
|
+
value="john"
|
|
32
|
+
checked={checkedList.includes('john')}
|
|
33
|
+
/>{' '}
|
|
34
|
+
John
|
|
35
|
+
<input
|
|
36
|
+
type="checkbox"
|
|
37
|
+
name="checkname"
|
|
38
|
+
onChange={handleInput}
|
|
39
|
+
value="sara"
|
|
40
|
+
checked={checkedList.includes('sara')}
|
|
41
|
+
/>{' '}
|
|
42
|
+
Sara
|
|
43
|
+
<input
|
|
44
|
+
type="checkbox"
|
|
45
|
+
name="checkname"
|
|
46
|
+
onChange={handleInput}
|
|
47
|
+
value="tom"
|
|
48
|
+
checked={checkedList.includes('tom')}
|
|
49
|
+
/>{' '}
|
|
50
|
+
Tom
|
|
51
|
+
</>
|
|
52
|
+
);
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
const testWrap =
|
|
56
|
+
document.getElementById('root') || document.createElement('div');
|
|
57
|
+
|
|
58
|
+
render(<Checkbox />, testWrap);
|
|
59
|
+
|
|
60
|
+
if (import.meta.vitest) {
|
|
61
|
+
const { it, expect } = import.meta.vitest;
|
|
62
|
+
it('You should see the initial value of the checkbox.', () => {
|
|
63
|
+
expect(testWrap.outerHTML).toBe(
|
|
64
|
+
'<div><input type="checkbox" name="checkname" value="john"> John<input type="checkbox" name="checkname" value="sara"> Sara<input type="checkbox" name="checkname" value="tom"> Tom</div>'
|
|
65
|
+
);
|
|
66
|
+
expect(
|
|
67
|
+
(
|
|
68
|
+
Array.from(
|
|
69
|
+
testWrap.querySelectorAll('input[name="checkname"]:checked')
|
|
70
|
+
) as HTMLFormElement[]
|
|
71
|
+
).map(item => item.value)
|
|
72
|
+
).toEqual(['sara']);
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
it('The changed value should be reflected in the checkbox.', () => {
|
|
76
|
+
if (testChangeRef.value) {
|
|
77
|
+
testChangeRef.value(['john', 'tom']);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
nextTick().then(() => {
|
|
81
|
+
expect(
|
|
82
|
+
(
|
|
83
|
+
Array.from(
|
|
84
|
+
testWrap.querySelectorAll('input[name="checkname"]:checked')
|
|
85
|
+
) as HTMLFormElement[]
|
|
86
|
+
).map(item => item.value)
|
|
87
|
+
).toEqual(['john', 'tom']);
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { h, Fragment, render, mount, ref, nextTick } from '@/index';
|
|
2
|
+
const testChangeRef = ref<null | ((newText: string) => void)>(null);
|
|
3
|
+
|
|
4
|
+
const Radio = mount(r => {
|
|
5
|
+
let text = 'sara';
|
|
6
|
+
|
|
7
|
+
const handleInput = (event: InputEvent) => {
|
|
8
|
+
text = (event.target as HTMLInputElement).value;
|
|
9
|
+
r();
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
testChangeRef.value = (newText: string) => {
|
|
13
|
+
text = newText;
|
|
14
|
+
r();
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
return () => (
|
|
18
|
+
<>
|
|
19
|
+
<input
|
|
20
|
+
type="radio"
|
|
21
|
+
name="checkname"
|
|
22
|
+
onChange={handleInput}
|
|
23
|
+
value="john"
|
|
24
|
+
checked={text === 'john'}
|
|
25
|
+
/>{' '}
|
|
26
|
+
John
|
|
27
|
+
<input
|
|
28
|
+
type="radio"
|
|
29
|
+
name="checkname"
|
|
30
|
+
onChange={handleInput}
|
|
31
|
+
value="sara"
|
|
32
|
+
checked={text === 'sara'}
|
|
33
|
+
/>{' '}
|
|
34
|
+
Sara
|
|
35
|
+
<input
|
|
36
|
+
type="radio"
|
|
37
|
+
name="checkname"
|
|
38
|
+
onChange={handleInput}
|
|
39
|
+
value="tom"
|
|
40
|
+
checked={text === 'tom'}
|
|
41
|
+
/>{' '}
|
|
42
|
+
Tom
|
|
43
|
+
</>
|
|
44
|
+
);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
const testWrap =
|
|
48
|
+
document.getElementById('root') || document.createElement('div');
|
|
49
|
+
|
|
50
|
+
render(<Radio />, testWrap);
|
|
51
|
+
|
|
52
|
+
if (import.meta.vitest) {
|
|
53
|
+
const { it, expect } = import.meta.vitest;
|
|
54
|
+
it('You should see the initial value of the radio.', () => {
|
|
55
|
+
expect(testWrap.outerHTML).toBe(
|
|
56
|
+
'<div><input type="radio" name="checkname" value="john"> John<input type="radio" name="checkname" value="sara"> Sara<input type="radio" name="checkname" value="tom"> Tom</div>'
|
|
57
|
+
);
|
|
58
|
+
expect(
|
|
59
|
+
(
|
|
60
|
+
Array.from(
|
|
61
|
+
testWrap.querySelectorAll('input[name="checkname"]:checked')
|
|
62
|
+
) as HTMLFormElement[]
|
|
63
|
+
).map(item => item.value)
|
|
64
|
+
).toEqual(['sara']);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it('The changed value should be reflected in the radio.', () => {
|
|
68
|
+
if (testChangeRef.value) {
|
|
69
|
+
testChangeRef.value('tom');
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
nextTick().then(() => {
|
|
73
|
+
expect(
|
|
74
|
+
(
|
|
75
|
+
Array.from(
|
|
76
|
+
testWrap.querySelectorAll('input[name="checkname"]:checked')
|
|
77
|
+
) as HTMLFormElement[]
|
|
78
|
+
).map(item => item.value)
|
|
79
|
+
).toEqual(['tom']);
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
}
|