@cfasim-ui/components 0.1.9 → 0.1.10

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 (76) hide show
  1. package/dist/Box/Box.d.ts +24 -0
  2. package/dist/Box/Box.spec.d.ts +1 -0
  3. package/dist/Box/Box.test.d.ts +1 -0
  4. package/dist/Button/Button.d.ts +29 -0
  5. package/dist/Button/Button.spec.d.ts +1 -0
  6. package/dist/Button/Button.test.d.ts +1 -0
  7. package/dist/Expander/Expander.d.ts +28 -0
  8. package/dist/Expander/Expander.spec.d.ts +1 -0
  9. package/dist/Hint/Hint.d.ts +5 -0
  10. package/dist/Hint/Hint.spec.d.ts +1 -0
  11. package/dist/Hint/Hint.test.d.ts +1 -0
  12. package/dist/Icon/Icon.d.ts +18 -0
  13. package/dist/Icon/Icon.spec.d.ts +1 -0
  14. package/dist/LightDarkToggle/LightDarkToggle.d.ts +2 -0
  15. package/dist/NumberInput/NumberInput.d.ts +21 -0
  16. package/dist/NumberInput/NumberInput.spec.d.ts +1 -0
  17. package/dist/NumberInput/NumberInput.test.d.ts +1 -0
  18. package/dist/SelectBox/SelectBox.d.ts +19 -0
  19. package/dist/SelectBox/SelectBox.spec.d.ts +1 -0
  20. package/dist/SelectBox/SelectBox.test.d.ts +1 -0
  21. package/dist/SidebarLayout/SidebarLayout.d.ts +37 -0
  22. package/dist/SidebarLayout/SidebarLayout.test.d.ts +1 -0
  23. package/dist/Spinner/Spinner.d.ts +9 -0
  24. package/dist/Spinner/Spinner.spec.d.ts +1 -0
  25. package/dist/TextInput/TextInput.d.ts +14 -0
  26. package/dist/TextInput/TextInput.spec.d.ts +1 -0
  27. package/dist/TextInput/TextInput.test.d.ts +1 -0
  28. package/dist/Toggle/Toggle.d.ts +14 -0
  29. package/dist/Toggle/Toggle.spec.d.ts +1 -0
  30. package/dist/Toggle/Toggle.test.d.ts +1 -0
  31. package/dist/index.css +2 -0
  32. package/dist/index.d.ts +15 -0
  33. package/dist/index.js +700 -0
  34. package/package.json +17 -3
  35. package/src/Box/Box.md +0 -41
  36. package/src/Box/Box.spec.ts +0 -13
  37. package/src/Box/Box.test.ts +0 -49
  38. package/src/Box/Box.vue +0 -52
  39. package/src/Button/Button.md +0 -55
  40. package/src/Button/Button.spec.ts +0 -18
  41. package/src/Button/Button.test.ts +0 -36
  42. package/src/Button/Button.vue +0 -81
  43. package/src/Expander/Expander.md +0 -23
  44. package/src/Expander/Expander.spec.ts +0 -14
  45. package/src/Expander/Expander.vue +0 -95
  46. package/src/Hint/Hint.md +0 -24
  47. package/src/Hint/Hint.spec.ts +0 -12
  48. package/src/Hint/Hint.test.ts +0 -34
  49. package/src/Hint/Hint.vue +0 -83
  50. package/src/Icon/Icon.md +0 -55
  51. package/src/Icon/Icon.spec.ts +0 -9
  52. package/src/Icon/Icon.vue +0 -112
  53. package/src/LightDarkToggle/LightDarkToggle.vue +0 -49
  54. package/src/NumberInput/NumberInput.md +0 -187
  55. package/src/NumberInput/NumberInput.spec.ts +0 -10
  56. package/src/NumberInput/NumberInput.test.ts +0 -580
  57. package/src/NumberInput/NumberInput.vue +0 -446
  58. package/src/SelectBox/SelectBox.md +0 -56
  59. package/src/SelectBox/SelectBox.spec.ts +0 -9
  60. package/src/SelectBox/SelectBox.test.ts +0 -42
  61. package/src/SelectBox/SelectBox.vue +0 -190
  62. package/src/SidebarLayout/SidebarLayout.md +0 -104
  63. package/src/SidebarLayout/SidebarLayout.test.ts +0 -86
  64. package/src/SidebarLayout/SidebarLayout.vue +0 -465
  65. package/src/Spinner/Spinner.md +0 -45
  66. package/src/Spinner/Spinner.spec.ts +0 -9
  67. package/src/Spinner/Spinner.vue +0 -55
  68. package/src/TextInput/TextInput.md +0 -41
  69. package/src/TextInput/TextInput.spec.ts +0 -10
  70. package/src/TextInput/TextInput.test.ts +0 -70
  71. package/src/TextInput/TextInput.vue +0 -90
  72. package/src/Toggle/Toggle.md +0 -68
  73. package/src/Toggle/Toggle.spec.ts +0 -13
  74. package/src/Toggle/Toggle.test.ts +0 -35
  75. package/src/Toggle/Toggle.vue +0 -81
  76. package/src/index.ts +0 -15
@@ -1,580 +0,0 @@
1
- import { describe, expect, it, vi } from "vitest";
2
- import { mount } from "@vue/test-utils";
3
- import NumberInput from "./NumberInput.vue";
4
-
5
- describe("NumberInput", () => {
6
- it("renders hint trigger when hint prop is provided", () => {
7
- const wrapper = mount(NumberInput, {
8
- props: {
9
- modelValue: 1000,
10
- label: "Population",
11
- hint: "Population must be between 1,000 and 100,000.",
12
- },
13
- });
14
-
15
- const hintButton = wrapper.find(".HintTrigger");
16
- expect(hintButton.exists()).toBe(true);
17
- });
18
-
19
- it("renders a label element when label prop is provided", () => {
20
- const wrapper = mount(NumberInput, {
21
- props: {
22
- modelValue: 42,
23
- label: "Population",
24
- },
25
- });
26
-
27
- const label = wrapper.find("label.input-label");
28
- expect(label.exists()).toBe(true);
29
- expect(label.text()).toContain("Population");
30
- expect(label.find("input").exists()).toBe(true);
31
- });
32
-
33
- it("does not render a label element when label is not provided", () => {
34
- const wrapper = mount(NumberInput, {
35
- props: {
36
- modelValue: 42,
37
- },
38
- });
39
-
40
- expect(wrapper.find("label").exists()).toBe(false);
41
- expect(wrapper.find("input").exists()).toBe(true);
42
- });
43
-
44
- it("does not render hint trigger when hint is not provided", () => {
45
- const wrapper = mount(NumberInput, {
46
- props: {
47
- modelValue: 1000,
48
- },
49
- });
50
-
51
- expect(wrapper.find(".HintTrigger").exists()).toBe(false);
52
- });
53
-
54
- it("does not emit update on typing, only on blur", async () => {
55
- const wrapper = mount(NumberInput, {
56
- props: {
57
- modelValue: 100,
58
- label: "Count",
59
- "onUpdate:modelValue": (v: number | undefined) =>
60
- wrapper.setProps({ modelValue: v }),
61
- },
62
- });
63
-
64
- const input = wrapper.find("input");
65
- await input.setValue(200);
66
- // Model should not have updated yet
67
- expect(wrapper.props("modelValue")).toBe(100);
68
-
69
- await input.trigger("blur");
70
- expect(wrapper.props("modelValue")).toBe(200);
71
- });
72
-
73
- it("emits update on Enter keydown", async () => {
74
- const wrapper = mount(NumberInput, {
75
- props: {
76
- modelValue: 100,
77
- label: "Count",
78
- "onUpdate:modelValue": (v: number | undefined) =>
79
- wrapper.setProps({ modelValue: v }),
80
- },
81
- });
82
-
83
- const input = wrapper.find("input");
84
- await input.setValue(300);
85
- expect(wrapper.props("modelValue")).toBe(100);
86
-
87
- await input.trigger("keydown.enter");
88
- expect(wrapper.props("modelValue")).toBe(300);
89
- });
90
-
91
- it("displays fraction as percentage when percent prop is set", () => {
92
- const wrapper = mount(NumberInput, {
93
- props: { modelValue: 0.91, label: "Immunity", percent: true },
94
- });
95
- const input = wrapper.find("input");
96
- expect((input.element as HTMLInputElement).value).toBe("91");
97
- expect(wrapper.find(".input-suffix").text()).toBe("%");
98
- });
99
-
100
- it("commits displayed percentage back as fraction", async () => {
101
- const wrapper = mount(NumberInput, {
102
- props: {
103
- modelValue: 0.5,
104
- label: "Rate",
105
- percent: true,
106
- "onUpdate:modelValue": (v: number | undefined) =>
107
- wrapper.setProps({ modelValue: v }),
108
- },
109
- });
110
- const input = wrapper.find("input");
111
- await input.setValue(85);
112
- await input.trigger("blur");
113
- expect(wrapper.props("modelValue")).toBeCloseTo(0.85);
114
- });
115
-
116
- it("validates min/max for percent mode", async () => {
117
- const wrapper = mount(NumberInput, {
118
- props: {
119
- modelValue: 0.5,
120
- label: "Rate",
121
- percent: true,
122
- "onUpdate:modelValue": (v: number | undefined) =>
123
- wrapper.setProps({ modelValue: v }),
124
- },
125
- });
126
- const input = wrapper.find("input");
127
-
128
- await input.setValue(110);
129
- await input.trigger("blur");
130
- expect(wrapper.find(".input-error").text()).toBe("Max 100%");
131
- expect(wrapper.props("modelValue")).toBe(0.5);
132
-
133
- await input.setValue(-5);
134
- await input.trigger("blur");
135
- expect(wrapper.find(".input-error").text()).toBe("Min 0%");
136
- expect(wrapper.props("modelValue")).toBe(0.5);
137
- });
138
-
139
- it("does not show suffix when percent is not set", () => {
140
- const wrapper = mount(NumberInput, {
141
- props: { modelValue: 42, label: "Count" },
142
- });
143
- expect(wrapper.find(".input-suffix").exists()).toBe(false);
144
- });
145
-
146
- it("shows error and does not commit when value exceeds max", async () => {
147
- const wrapper = mount(NumberInput, {
148
- props: {
149
- modelValue: 50,
150
- label: "Count",
151
- max: 100,
152
- "onUpdate:modelValue": (v: number | undefined) =>
153
- wrapper.setProps({ modelValue: v }),
154
- },
155
- });
156
-
157
- const input = wrapper.find("input");
158
- await input.setValue(200);
159
- await input.trigger("blur");
160
-
161
- expect(wrapper.props("modelValue")).toBe(50);
162
- expect(wrapper.find(".input-error").exists()).toBe(true);
163
- expect(wrapper.find(".input-error").text()).toBe("Max 100");
164
- expect(
165
- (input.element as HTMLInputElement).getAttribute("aria-invalid"),
166
- ).toBe("true");
167
- });
168
-
169
- it("shows error and does not commit when value is below min", async () => {
170
- const wrapper = mount(NumberInput, {
171
- props: {
172
- modelValue: 50,
173
- label: "Count",
174
- min: 10,
175
- "onUpdate:modelValue": (v: number | undefined) =>
176
- wrapper.setProps({ modelValue: v }),
177
- },
178
- });
179
-
180
- const input = wrapper.find("input");
181
- await input.setValue(5);
182
- await input.trigger("blur");
183
-
184
- expect(wrapper.props("modelValue")).toBe(50);
185
- expect(wrapper.find(".input-error").text()).toBe("Min 10");
186
- });
187
-
188
- it("clears error when valid value is committed", async () => {
189
- const wrapper = mount(NumberInput, {
190
- props: {
191
- modelValue: 50,
192
- label: "Count",
193
- max: 100,
194
- "onUpdate:modelValue": (v: number | undefined) =>
195
- wrapper.setProps({ modelValue: v }),
196
- },
197
- });
198
-
199
- const input = wrapper.find("input");
200
- await input.setValue(200);
201
- await input.trigger("blur");
202
- expect(wrapper.find(".input-error").exists()).toBe(true);
203
-
204
- await input.setValue(80);
205
- await input.trigger("blur");
206
- expect(wrapper.find(".input-error").exists()).toBe(false);
207
- expect(wrapper.props("modelValue")).toBe(80);
208
- });
209
-
210
- it("shows percent suffix in error message for percent mode", async () => {
211
- const wrapper = mount(NumberInput, {
212
- props: {
213
- modelValue: 0.5,
214
- label: "Rate",
215
- percent: true,
216
- max: 0.99,
217
- "onUpdate:modelValue": (v: number | undefined) =>
218
- wrapper.setProps({ modelValue: v }),
219
- },
220
- });
221
-
222
- const input = wrapper.find("input");
223
- await input.setValue(100);
224
- await input.trigger("blur");
225
-
226
- expect(wrapper.props("modelValue")).toBe(0.5);
227
- expect(wrapper.find(".input-error").text()).toBe("Max 99%");
228
- });
229
-
230
- it("slider with live prop emits updates while dragging", async () => {
231
- const updates: number[] = [];
232
- const wrapper = mount(NumberInput, {
233
- props: {
234
- modelValue: 50,
235
- label: "Count",
236
- slider: true,
237
- live: true,
238
- min: 0,
239
- max: 100,
240
- "onUpdate:modelValue": (v: number | undefined) => {
241
- if (v != null) updates.push(v);
242
- wrapper.setProps({ modelValue: v });
243
- },
244
- },
245
- });
246
-
247
- const slider = wrapper.findComponent({ name: "SliderRoot" });
248
- expect(slider.exists()).toBe(true);
249
-
250
- // Simulate slider update (as if dragging)
251
- slider.vm.$emit("update:modelValue", [75]);
252
- expect(updates).toContain(75);
253
- });
254
-
255
- it("slider without live prop does not emit on update", async () => {
256
- const updates: number[] = [];
257
- const wrapper = mount(NumberInput, {
258
- props: {
259
- modelValue: 50,
260
- label: "Count",
261
- slider: true,
262
- min: 0,
263
- max: 100,
264
- "onUpdate:modelValue": (v: number | undefined) => {
265
- if (v != null) updates.push(v);
266
- wrapper.setProps({ modelValue: v });
267
- },
268
- },
269
- });
270
-
271
- const slider = wrapper.findComponent({ name: "SliderRoot" });
272
- slider.vm.$emit("update:modelValue", [75]);
273
- expect(updates).not.toContain(75);
274
- });
275
-
276
- it("live input commits immediately on change event (spinner/arrow keys)", async () => {
277
- const wrapper = mount(NumberInput, {
278
- props: {
279
- modelValue: 100,
280
- label: "Count",
281
- live: true,
282
- "onUpdate:modelValue": (v: number | undefined) =>
283
- wrapper.setProps({ modelValue: v }),
284
- },
285
- });
286
-
287
- const input = wrapper.find("input");
288
- await input.setValue(200);
289
- await input.trigger("change");
290
- expect(wrapper.props("modelValue")).toBe(200);
291
- });
292
-
293
- it("live input debounces on typing (input event only)", async () => {
294
- vi.useFakeTimers();
295
- const updates: number[] = [];
296
- const wrapper = mount(NumberInput, {
297
- props: {
298
- modelValue: 100,
299
- label: "Count",
300
- live: true,
301
- "onUpdate:modelValue": (v: number | undefined) => {
302
- if (v != null) updates.push(v);
303
- wrapper.setProps({ modelValue: v });
304
- },
305
- },
306
- });
307
-
308
- const input = wrapper.find("input");
309
- // setValue triggers both input and change, so the change handler
310
- // commits immediately. To test debounce in isolation, we verify
311
- // that the debounce timer exists by checking two rapid changes:
312
- // the second should cancel the first's timer.
313
- await input.setValue(200);
314
- // change fires immediately, so 200 is committed
315
- expect(updates).toContain(200);
316
-
317
- // Now simulate rapid typing: two changes in quick succession
318
- // Both trigger immediate commit via change, confirming live works
319
- await input.setValue(300);
320
- await input.setValue(400);
321
- expect(updates).toContain(400);
322
-
323
- vi.useRealTimers();
324
- });
325
-
326
- it("increments value on ArrowUp", async () => {
327
- const wrapper = mount(NumberInput, {
328
- props: {
329
- modelValue: 100,
330
- label: "Count",
331
- "onUpdate:modelValue": (v: number | undefined) =>
332
- wrapper.setProps({ modelValue: v }),
333
- },
334
- });
335
- const input = wrapper.find("input");
336
- await input.trigger("keydown", { key: "ArrowUp" });
337
- expect(wrapper.props("modelValue")).toBe(101);
338
- expect((input.element as HTMLInputElement).value).toBe("101");
339
- });
340
-
341
- it("decrements value on ArrowDown", async () => {
342
- const wrapper = mount(NumberInput, {
343
- props: {
344
- modelValue: 100,
345
- label: "Count",
346
- "onUpdate:modelValue": (v: number | undefined) =>
347
- wrapper.setProps({ modelValue: v }),
348
- },
349
- });
350
- const input = wrapper.find("input");
351
- await input.trigger("keydown", { key: "ArrowDown" });
352
- expect(wrapper.props("modelValue")).toBe(99);
353
- });
354
-
355
- it("steps by custom step prop", async () => {
356
- const wrapper = mount(NumberInput, {
357
- props: {
358
- modelValue: 100,
359
- label: "Count",
360
- step: 5,
361
- "onUpdate:modelValue": (v: number | undefined) =>
362
- wrapper.setProps({ modelValue: v }),
363
- },
364
- });
365
- const input = wrapper.find("input");
366
- await input.trigger("keydown", { key: "ArrowUp" });
367
- expect(wrapper.props("modelValue")).toBe(105);
368
- });
369
-
370
- it("steps by 10x with shift+arrow", async () => {
371
- const wrapper = mount(NumberInput, {
372
- props: {
373
- modelValue: 100,
374
- label: "Count",
375
- "onUpdate:modelValue": (v: number | undefined) =>
376
- wrapper.setProps({ modelValue: v }),
377
- },
378
- });
379
- const input = wrapper.find("input");
380
- await input.trigger("keydown", { key: "ArrowUp", shiftKey: true });
381
- expect(wrapper.props("modelValue")).toBe(110);
382
- });
383
-
384
- it("converts step prop to display units in percent mode", async () => {
385
- const wrapper = mount(NumberInput, {
386
- props: {
387
- modelValue: 0.5,
388
- label: "Rate",
389
- percent: true,
390
- step: 0.01,
391
- "onUpdate:modelValue": (v: number | undefined) =>
392
- wrapper.setProps({ modelValue: v }),
393
- },
394
- });
395
- const input = wrapper.find("input");
396
- expect((input.element as HTMLInputElement).value).toBe("50");
397
-
398
- await input.trigger("keydown", { key: "ArrowUp" });
399
- expect(wrapper.props("modelValue")).toBeCloseTo(0.51);
400
- expect((input.element as HTMLInputElement).value).toBe("51");
401
- });
402
-
403
- it("clamps arrow step to min/max", async () => {
404
- const wrapper = mount(NumberInput, {
405
- props: {
406
- modelValue: 99,
407
- label: "Count",
408
- max: 100,
409
- "onUpdate:modelValue": (v: number | undefined) =>
410
- wrapper.setProps({ modelValue: v }),
411
- },
412
- });
413
- const input = wrapper.find("input");
414
- await input.trigger("keydown", { key: "ArrowUp" });
415
- expect(wrapper.props("modelValue")).toBe(100);
416
- await input.trigger("keydown", { key: "ArrowUp" });
417
- expect(wrapper.props("modelValue")).toBe(100);
418
- });
419
-
420
- it("displays numbers with comma separators", () => {
421
- const wrapper = mount(NumberInput, {
422
- props: { modelValue: 1000000, label: "Population" },
423
- });
424
- const input = wrapper.find("input");
425
- expect((input.element as HTMLInputElement).value).toBe("1,000,000");
426
- });
427
-
428
- it("keeps commas during editing and commits on blur", async () => {
429
- const wrapper = mount(NumberInput, {
430
- props: {
431
- modelValue: 1500,
432
- label: "Count",
433
- "onUpdate:modelValue": (v: number | undefined) =>
434
- wrapper.setProps({ modelValue: v }),
435
- },
436
- });
437
- const input = wrapper.find("input");
438
- expect((input.element as HTMLInputElement).value).toBe("1,500");
439
-
440
- await input.setValue("2500");
441
- await input.trigger("blur");
442
- expect((input.element as HTMLInputElement).value).toBe("2,500");
443
- expect(wrapper.props("modelValue")).toBe(2500);
444
- });
445
-
446
- it("accepts comma-formatted input and parses correctly", async () => {
447
- const wrapper = mount(NumberInput, {
448
- props: {
449
- modelValue: 100,
450
- label: "Count",
451
- "onUpdate:modelValue": (v: number | undefined) =>
452
- wrapper.setProps({ modelValue: v }),
453
- },
454
- });
455
- const input = wrapper.find("input");
456
- await input.setValue("1,234,567");
457
- await input.trigger("blur");
458
- expect(wrapper.props("modelValue")).toBe(1234567);
459
- });
460
-
461
- it("does not reformat while typing a decimal point", async () => {
462
- const wrapper = mount(NumberInput, {
463
- props: {
464
- modelValue: 15,
465
- label: "Count",
466
- },
467
- });
468
- const input = wrapper.find("input");
469
- // Simulate typing "15." — reformatInput should not strip the trailing dot
470
- (input.element as HTMLInputElement).value = "15.";
471
- await input.trigger("input");
472
- expect((input.element as HTMLInputElement).value).toBe("15.");
473
-
474
- // Typing "15.60" — trailing zero after decimal should also be preserved
475
- (input.element as HTMLInputElement).value = "15.60";
476
- await input.trigger("input");
477
- expect((input.element as HTMLInputElement).value).toBe("15.60");
478
- });
479
-
480
- it("truncates decimal to integer when numberType is integer", async () => {
481
- const wrapper = mount(NumberInput, {
482
- props: {
483
- modelValue: 15,
484
- label: "Count",
485
- numberType: "integer" as const,
486
- "onUpdate:modelValue": (v: number | undefined) =>
487
- wrapper.setProps({ modelValue: v }),
488
- },
489
- });
490
- const input = wrapper.find("input");
491
- await input.setValue("15.6");
492
- await input.trigger("blur");
493
- expect(wrapper.props("modelValue")).toBe(15);
494
- expect((input.element as HTMLInputElement).value).toBe("15");
495
- });
496
-
497
- it("integer with percent allows whole percentages like 0.42", async () => {
498
- const wrapper = mount(NumberInput, {
499
- props: {
500
- modelValue: 0.5,
501
- label: "Rate",
502
- percent: true,
503
- numberType: "integer" as const,
504
- "onUpdate:modelValue": (v: number | undefined) =>
505
- wrapper.setProps({ modelValue: v }),
506
- },
507
- });
508
- const input = wrapper.find("input");
509
- // 42% → internal 0.42 (display value 42 is a whole number, so allowed)
510
- await input.setValue("42");
511
- await input.trigger("blur");
512
- expect(wrapper.props("modelValue")).toBeCloseTo(0.42);
513
- });
514
-
515
- it("integer with percent truncates fractional percentages", async () => {
516
- const wrapper = mount(NumberInput, {
517
- props: {
518
- modelValue: 0.5,
519
- label: "Rate",
520
- percent: true,
521
- numberType: "integer" as const,
522
- "onUpdate:modelValue": (v: number | undefined) =>
523
- wrapper.setProps({ modelValue: v }),
524
- },
525
- });
526
- const input = wrapper.find("input");
527
- // 42.7% should truncate display to 42 → internal 0.42
528
- await input.setValue("42.7");
529
- await input.trigger("blur");
530
- expect(wrapper.props("modelValue")).toBeCloseTo(0.42);
531
- expect((input.element as HTMLInputElement).value).toBe("42");
532
- });
533
-
534
- it("displays .0 suffix for whole numbers when numberType is float", () => {
535
- const wrapper = mount(NumberInput, {
536
- props: { modelValue: 100, label: "Rate", numberType: "float" as const },
537
- });
538
- const input = wrapper.find("input");
539
- expect((input.element as HTMLInputElement).value).toBe("100.0");
540
- });
541
-
542
- it("does not add .0 suffix for non-whole floats", () => {
543
- const wrapper = mount(NumberInput, {
544
- props: { modelValue: 2.5, label: "Rate", numberType: "float" as const },
545
- });
546
- const input = wrapper.find("input");
547
- expect((input.element as HTMLInputElement).value).toBe("2.5");
548
- });
549
-
550
- it("adds .0 suffix after committing a whole number in float mode", async () => {
551
- const wrapper = mount(NumberInput, {
552
- props: {
553
- modelValue: 2.5,
554
- label: "Rate",
555
- numberType: "float" as const,
556
- "onUpdate:modelValue": (v: number | undefined) =>
557
- wrapper.setProps({ modelValue: v }),
558
- },
559
- });
560
- const input = wrapper.find("input");
561
- await input.setValue("3");
562
- await input.trigger("blur");
563
- expect((input.element as HTMLInputElement).value).toBe("3.0");
564
- });
565
-
566
- it("syncs local value when model changes externally", async () => {
567
- const wrapper = mount(NumberInput, {
568
- props: {
569
- modelValue: 100,
570
- label: "Count",
571
- },
572
- });
573
-
574
- const input = wrapper.find("input");
575
- expect((input.element as HTMLInputElement).value).toBe("100");
576
-
577
- await wrapper.setProps({ modelValue: 5000 });
578
- expect((input.element as HTMLInputElement).value).toBe("5,000");
579
- });
580
- });