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.
Files changed (56) hide show
  1. package/README.md +51 -1
  2. package/dist/hook/mountCallback.d.ts +3 -1
  3. package/dist/hook/ref.d.ts +1 -0
  4. package/dist/index.d.ts +2 -2
  5. package/dist/lithent.mjs +67 -44
  6. package/dist/lithent.mjs.map +1 -1
  7. package/dist/lithent.umd.js +1 -1
  8. package/dist/lithent.umd.js.map +1 -1
  9. package/dist/types/index.d.ts +3 -2
  10. package/dist/utils/index.d.ts +1 -0
  11. package/dist/utils/predicator.d.ts +2 -1
  12. package/dist/utils/universalRef.d.ts +3 -9
  13. package/dist/wDom.d.ts +0 -1
  14. package/examples/dist/assets/index.2b484d3b.js +2 -0
  15. package/examples/dist/assets/index.2b484d3b.js.map +1 -0
  16. package/examples/dist/index.html +1 -1
  17. package/package.json +11 -5
  18. package/src/diff.ts +5 -12
  19. package/src/hook/mountCallback.ts +11 -4
  20. package/src/hook/ref.ts +1 -0
  21. package/src/hook/updateCallback.ts +1 -9
  22. package/src/index.ts +11 -2
  23. package/src/render.ts +48 -31
  24. package/src/{examples → tests}/callback.tsx +0 -1
  25. package/src/tests/example1.tsx +75 -0
  26. package/src/tests/example10.tsx +90 -0
  27. package/src/tests/example11.tsx +82 -0
  28. package/src/tests/example12.tsx +76 -0
  29. package/src/tests/example13.tsx +59 -0
  30. package/src/tests/example14.tsx +115 -0
  31. package/src/tests/example15.tsx +68 -0
  32. package/src/tests/example16.tsx +49 -0
  33. package/src/tests/example17.tsx +50 -0
  34. package/src/tests/example2.tsx +104 -0
  35. package/src/tests/example3.tsx +96 -0
  36. package/src/tests/example4.tsx +138 -0
  37. package/src/tests/example5.tsx +63 -0
  38. package/src/tests/example6.tsx +54 -0
  39. package/src/tests/example7.tsx +21 -0
  40. package/src/tests/example8.tsx +68 -0
  41. package/src/tests/example9.tsx +46 -0
  42. package/src/tests/renew.tsx +56 -0
  43. package/src/tests/svg.tsx +44 -0
  44. package/src/types/index.ts +12 -13
  45. package/src/utils/index.ts +3 -0
  46. package/src/utils/predicator.ts +6 -2
  47. package/src/utils/universalRef.ts +15 -9
  48. package/src/wDom.ts +5 -7
  49. package/examples/dist/assets/index.f15c0062.js +0 -2
  50. package/examples/dist/assets/index.f15c0062.js.map +0 -1
  51. package/src/examples/renew.tsx +0 -29
  52. /package/src/{examples → tests}/insert.tsx +0 -0
  53. /package/src/{examples → tests}/loop.tsx +0 -0
  54. /package/src/{examples → tests}/nested.tsx +0 -0
  55. /package/src/{examples → tests}/selectbox.tsx +0 -0
  56. /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.36.2",
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": "jest",
21
- "test:watch": "jest --watch",
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": ["lithent"],
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 { remakedChildren, unUsedChildren } = diffLoopChildren(
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
- const key = getKey(item);
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
- export const mountCallback = (effectAction: () => void) => {
11
- const mts = componentRef.get(getComponentKey())!.mts;
9
+ let mountedQueue: WDom[] = [];
12
10
 
13
- mts.push(effectAction);
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 +1,2 @@
1
1
  export const ref = <T>(initValue: T) => ({ value: initValue });
2
+ export const nextTick = () => Promise.resolve();
@@ -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 { h, Fragment, render, mount, mountCallback, updateCallback, ref };
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 { runMountedQueueFromWDom } from '@/hook/mountCallback';
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
- if (wDom.componentProps) {
38
- const component = componentRef.get(wDom.componentProps)?.vd.value;
39
- if (component) {
40
- runUnmountQueueFromWDom(component);
41
- recursiveRemoveEvent(component);
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 && ['fragment', 'loop'].includes(parentType)) {
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 && ['fragment', 'loop'].includes(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
- (element as HTMLElement).setAttribute(dataKey, String(dataValue));
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 === 'fragment' || type === 'loop';
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 = document.createElement(tag);
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
- runMountedQueueFromWDom(wDom);
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 === 'fragment' || vDom.type === 'loop';
424
+ const isVirtualType = checkVirtualType(vDom.type);
408
425
  if (vDom.isRoot && isVirtualType) {
409
426
  return vDom.wrapElement;
410
427
  }
@@ -61,7 +61,6 @@ const CallbackRoot = mount(renew => {
61
61
 
62
62
  const toggle = () => {
63
63
  isShow = !isShow;
64
- console.log(isShow);
65
64
  renew();
66
65
  };
67
66
 
@@ -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
+ }