vueless 1.3.9-beta.8 → 1.4.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/composables/useUI.ts +187 -155
- package/composables/useVirtualScroll.ts +100 -0
- package/package.json +2 -2
- package/types.ts +11 -0
- package/ui.button-link/ULink.vue +18 -2
- package/ui.data-table/UTable.vue +597 -198
- package/ui.data-table/UTableRow.vue +372 -168
- package/ui.data-table/config.ts +33 -5
- package/ui.data-table/storybook/stories.ts +163 -0
- package/ui.data-table/tests/UTable.test.ts +456 -20
- package/ui.data-table/tests/UTableRow.test.ts +35 -104
- package/ui.data-table/types.ts +57 -0
- package/ui.data-table/utilTable.ts +16 -10
- package/ui.form-checkbox/UCheckbox.vue +3 -7
- package/ui.text-notify/config.ts +1 -1
- package/ui.text-number/UNumber.vue +36 -2
- package/ui.text-number/config.ts +1 -0
- package/ui.text-number/storybook/stories.ts +11 -0
- package/ui.text-number/tests/UNumber.test.ts +90 -0
- package/ui.text-number/types.ts +5 -0
- package/utils/node/mergeConfigs.d.ts +2 -2
- package/utils/node/mergeConfigs.js +240 -121
- package/utils/node/vuelessConfig.d.ts +1 -1
|
@@ -291,6 +291,280 @@ describe("UTable.vue", () => {
|
|
|
291
291
|
expect(cell.attributes("class")).toContain(compactClasses);
|
|
292
292
|
});
|
|
293
293
|
});
|
|
294
|
+
|
|
295
|
+
it("Virtual Scroll – is disabled by default", () => {
|
|
296
|
+
const component = mountUTable(getDefaultProps());
|
|
297
|
+
|
|
298
|
+
expect(component.vm.$props.virtualScroll).toBe(false);
|
|
299
|
+
});
|
|
300
|
+
|
|
301
|
+
it("Virtual Scroll – enables virtual scrolling when virtualScroll is true", () => {
|
|
302
|
+
const component = mountUTable(
|
|
303
|
+
getDefaultProps({
|
|
304
|
+
virtualScroll: true,
|
|
305
|
+
}),
|
|
306
|
+
);
|
|
307
|
+
|
|
308
|
+
expect(component.vm.$props.virtualScroll).toBe(true);
|
|
309
|
+
|
|
310
|
+
// Check that the table wrapper has virtual scroll classes
|
|
311
|
+
const tableWrapper = component.find("table").element.parentElement;
|
|
312
|
+
|
|
313
|
+
expect(tableWrapper?.className).toContain("overflow-y-auto");
|
|
314
|
+
});
|
|
315
|
+
|
|
316
|
+
it("Virtual Scroll – renders spacer rows when virtualScroll is enabled", async () => {
|
|
317
|
+
const manyRows = Array.from({ length: 100 }, (_, i) => ({
|
|
318
|
+
id: String(i + 1),
|
|
319
|
+
name: `User ${i + 1}`,
|
|
320
|
+
email: `user${i + 1}@example.com`,
|
|
321
|
+
role: "User",
|
|
322
|
+
}));
|
|
323
|
+
|
|
324
|
+
const component = mountUTable(
|
|
325
|
+
getDefaultProps({
|
|
326
|
+
rows: manyRows,
|
|
327
|
+
virtualScroll: true,
|
|
328
|
+
rowHeight: 40,
|
|
329
|
+
}),
|
|
330
|
+
);
|
|
331
|
+
|
|
332
|
+
await nextTick();
|
|
333
|
+
|
|
334
|
+
const allRows = component.findAll("tbody tr");
|
|
335
|
+
const spacerRows = allRows.filter((row) => {
|
|
336
|
+
const td = row.find("td");
|
|
337
|
+
const hasTableRow = row.findComponent(UTableRow).exists();
|
|
338
|
+
|
|
339
|
+
return (
|
|
340
|
+
td.exists() &&
|
|
341
|
+
td.attributes("colspan") &&
|
|
342
|
+
td.attributes("style")?.includes("height") &&
|
|
343
|
+
!hasTableRow
|
|
344
|
+
);
|
|
345
|
+
});
|
|
346
|
+
|
|
347
|
+
// Should have at least one spacer row (top or bottom)
|
|
348
|
+
expect(spacerRows.length).toBeGreaterThanOrEqual(1);
|
|
349
|
+
});
|
|
350
|
+
|
|
351
|
+
it("Row Height – uses default rowHeight value", () => {
|
|
352
|
+
const component = mountUTable(getDefaultProps());
|
|
353
|
+
|
|
354
|
+
expect(component.vm.$props.rowHeight).toBe(40);
|
|
355
|
+
});
|
|
356
|
+
|
|
357
|
+
it("Row Height – accepts custom rowHeight value", () => {
|
|
358
|
+
const customRowHeight = 60;
|
|
359
|
+
|
|
360
|
+
const component = mountUTable(
|
|
361
|
+
getDefaultProps({
|
|
362
|
+
rowHeight: customRowHeight,
|
|
363
|
+
}),
|
|
364
|
+
);
|
|
365
|
+
|
|
366
|
+
expect(component.vm.$props.rowHeight).toBe(customRowHeight);
|
|
367
|
+
});
|
|
368
|
+
|
|
369
|
+
it("Buffer Size – uses default bufferSize value", () => {
|
|
370
|
+
const component = mountUTable(getDefaultProps());
|
|
371
|
+
|
|
372
|
+
expect(component.vm.$props.bufferSize).toBe(10);
|
|
373
|
+
});
|
|
374
|
+
|
|
375
|
+
it("Buffer Size – accepts custom bufferSize value", () => {
|
|
376
|
+
const customBufferSize = 20;
|
|
377
|
+
|
|
378
|
+
const component = mountUTable(
|
|
379
|
+
getDefaultProps({
|
|
380
|
+
bufferSize: customBufferSize,
|
|
381
|
+
}),
|
|
382
|
+
);
|
|
383
|
+
|
|
384
|
+
expect(component.vm.$props.bufferSize).toBe(customBufferSize);
|
|
385
|
+
});
|
|
386
|
+
|
|
387
|
+
it("Virtual Scroll – renders only visible rows plus buffer", async () => {
|
|
388
|
+
const manyRows = Array.from({ length: 100 }, (_, i) => ({
|
|
389
|
+
id: String(i + 1),
|
|
390
|
+
name: `User ${i + 1}`,
|
|
391
|
+
email: `user${i + 1}@example.com`,
|
|
392
|
+
role: "User",
|
|
393
|
+
}));
|
|
394
|
+
|
|
395
|
+
const component = mountUTable(
|
|
396
|
+
getDefaultProps({
|
|
397
|
+
rows: manyRows,
|
|
398
|
+
virtualScroll: true,
|
|
399
|
+
rowHeight: 40,
|
|
400
|
+
scrollHeight: "400px",
|
|
401
|
+
bufferSize: 5,
|
|
402
|
+
}),
|
|
403
|
+
);
|
|
404
|
+
|
|
405
|
+
await nextTick();
|
|
406
|
+
|
|
407
|
+
const tableRows = component.findAllComponents(UTableRow);
|
|
408
|
+
|
|
409
|
+
// Should render less than total rows (only visible + buffer)
|
|
410
|
+
expect(tableRows.length).toBeLessThan(manyRows.length);
|
|
411
|
+
expect(tableRows.length).toBeGreaterThan(0);
|
|
412
|
+
});
|
|
413
|
+
|
|
414
|
+
it("Virtual Scroll – renders all rows when virtualScroll is false", () => {
|
|
415
|
+
const manyRows = Array.from({ length: 50 }, (_, i) => ({
|
|
416
|
+
id: String(i + 1),
|
|
417
|
+
name: `User ${i + 1}`,
|
|
418
|
+
email: `user${i + 1}@example.com`,
|
|
419
|
+
role: "User",
|
|
420
|
+
}));
|
|
421
|
+
|
|
422
|
+
const component = mountUTable(
|
|
423
|
+
getDefaultProps({
|
|
424
|
+
rows: manyRows,
|
|
425
|
+
virtualScroll: false,
|
|
426
|
+
}),
|
|
427
|
+
);
|
|
428
|
+
|
|
429
|
+
const tableRows = component.findAllComponents(UTableRow);
|
|
430
|
+
|
|
431
|
+
expect(tableRows.length).toBe(manyRows.length);
|
|
432
|
+
});
|
|
433
|
+
|
|
434
|
+
it("Scroll Height – accepts scrollHeight prop", () => {
|
|
435
|
+
const scrollHeight = "500px";
|
|
436
|
+
|
|
437
|
+
const component = mountUTable(
|
|
438
|
+
getDefaultProps({
|
|
439
|
+
virtualScroll: true,
|
|
440
|
+
scrollHeight,
|
|
441
|
+
}),
|
|
442
|
+
);
|
|
443
|
+
|
|
444
|
+
expect(component.vm.$props.scrollHeight).toBe(scrollHeight);
|
|
445
|
+
});
|
|
446
|
+
|
|
447
|
+
it("Search – uses default empty search value", () => {
|
|
448
|
+
const component = mountUTable(getDefaultProps());
|
|
449
|
+
|
|
450
|
+
expect(component.vm.$props.search).toBe("");
|
|
451
|
+
});
|
|
452
|
+
|
|
453
|
+
it("Search – accepts search string", () => {
|
|
454
|
+
const searchQuery = "john";
|
|
455
|
+
|
|
456
|
+
const component = mountUTable(
|
|
457
|
+
getDefaultProps({
|
|
458
|
+
search: searchQuery,
|
|
459
|
+
}),
|
|
460
|
+
);
|
|
461
|
+
|
|
462
|
+
expect(component.vm.$props.search).toBe(searchQuery);
|
|
463
|
+
});
|
|
464
|
+
|
|
465
|
+
it("Search – emits search event with total matches count", async () => {
|
|
466
|
+
const component = mountUTable(getDefaultProps());
|
|
467
|
+
|
|
468
|
+
await component.setProps({ search: "doe" });
|
|
469
|
+
await nextTick();
|
|
470
|
+
|
|
471
|
+
expect(component.emitted("search")).toBeTruthy();
|
|
472
|
+
expect(component.emitted("search")![0][0]).toBe(1);
|
|
473
|
+
});
|
|
474
|
+
|
|
475
|
+
it("Search – emits search event with zero when no matches found", async () => {
|
|
476
|
+
const component = mountUTable(getDefaultProps());
|
|
477
|
+
|
|
478
|
+
// First set a search that has matches
|
|
479
|
+
await component.setProps({ search: "doe" });
|
|
480
|
+
await nextTick();
|
|
481
|
+
|
|
482
|
+
// Then set a search with no matches
|
|
483
|
+
await component.setProps({ search: "nonexistent" });
|
|
484
|
+
await nextTick();
|
|
485
|
+
|
|
486
|
+
const emittedEvents = component.emitted("search");
|
|
487
|
+
|
|
488
|
+
expect(emittedEvents).toBeTruthy();
|
|
489
|
+
expect(emittedEvents![emittedEvents!.length - 1][0]).toBe(0);
|
|
490
|
+
});
|
|
491
|
+
|
|
492
|
+
it("Search – finds multiple matches across different rows", async () => {
|
|
493
|
+
const component = mountUTable(getDefaultProps());
|
|
494
|
+
|
|
495
|
+
await component.setProps({ search: "example" });
|
|
496
|
+
await nextTick();
|
|
497
|
+
|
|
498
|
+
const emittedEvents = component.emitted("search");
|
|
499
|
+
|
|
500
|
+
expect(emittedEvents).toBeTruthy();
|
|
501
|
+
expect(emittedEvents![0][0]).toBe(3); // All 3 rows have "example.com" in email
|
|
502
|
+
});
|
|
503
|
+
|
|
504
|
+
it("Search – is case insensitive", async () => {
|
|
505
|
+
const component = mountUTable(getDefaultProps());
|
|
506
|
+
|
|
507
|
+
await component.setProps({ search: "DOE" });
|
|
508
|
+
await nextTick();
|
|
509
|
+
|
|
510
|
+
const emittedEvents = component.emitted("search");
|
|
511
|
+
|
|
512
|
+
expect(emittedEvents).toBeTruthy();
|
|
513
|
+
expect(emittedEvents![0][0]).toBe(1);
|
|
514
|
+
|
|
515
|
+
// Clear search first to trigger a new event
|
|
516
|
+
await component.setProps({ search: "" });
|
|
517
|
+
await nextTick();
|
|
518
|
+
|
|
519
|
+
await component.setProps({ search: "doe" });
|
|
520
|
+
await nextTick();
|
|
521
|
+
|
|
522
|
+
expect(emittedEvents![emittedEvents!.length - 1][0]).toBe(1);
|
|
523
|
+
});
|
|
524
|
+
|
|
525
|
+
it("Search – finds partial matches", async () => {
|
|
526
|
+
const component = mountUTable(getDefaultProps());
|
|
527
|
+
|
|
528
|
+
await component.setProps({ search: "Jo" });
|
|
529
|
+
await nextTick();
|
|
530
|
+
|
|
531
|
+
expect(component.emitted("search")![0][0]).toBeGreaterThan(0);
|
|
532
|
+
});
|
|
533
|
+
|
|
534
|
+
it("Search Match – uses default searchMatch value", () => {
|
|
535
|
+
const component = mountUTable(getDefaultProps());
|
|
536
|
+
|
|
537
|
+
expect(component.vm.$props.searchMatch).toBe(-1);
|
|
538
|
+
});
|
|
539
|
+
|
|
540
|
+
it("Search Match – accepts searchMatch index", () => {
|
|
541
|
+
const searchMatchIndex = 2;
|
|
542
|
+
|
|
543
|
+
const component = mountUTable(
|
|
544
|
+
getDefaultProps({
|
|
545
|
+
search: "example",
|
|
546
|
+
searchMatch: searchMatchIndex,
|
|
547
|
+
}),
|
|
548
|
+
);
|
|
549
|
+
|
|
550
|
+
expect(component.vm.$props.searchMatch).toBe(searchMatchIndex);
|
|
551
|
+
});
|
|
552
|
+
|
|
553
|
+
it("Search Match – passes search props to table rows", async () => {
|
|
554
|
+
const searchQuery = "john";
|
|
555
|
+
|
|
556
|
+
const component = mountUTable(
|
|
557
|
+
getDefaultProps({
|
|
558
|
+
search: searchQuery,
|
|
559
|
+
}),
|
|
560
|
+
);
|
|
561
|
+
|
|
562
|
+
await nextTick();
|
|
563
|
+
|
|
564
|
+
const tableRow = component.getComponent(UTableRow);
|
|
565
|
+
|
|
566
|
+
expect(tableRow.props("search")).toBe(searchQuery);
|
|
567
|
+
});
|
|
294
568
|
});
|
|
295
569
|
|
|
296
570
|
describe("Slots", () => {
|
|
@@ -469,7 +743,7 @@ describe("UTable.vue", () => {
|
|
|
469
743
|
await firstRow.trigger("click");
|
|
470
744
|
|
|
471
745
|
expect(component.emitted("clickRow")).toBeTruthy();
|
|
472
|
-
expect(component.emitted("clickRow")![0][0]).
|
|
746
|
+
expect(component.emitted("clickRow")![0][0]).toMatchObject(defaultRows[0]);
|
|
473
747
|
});
|
|
474
748
|
|
|
475
749
|
it("Double Click Row – emits doubleClickRow event when row is double-clicked", async () => {
|
|
@@ -480,7 +754,7 @@ describe("UTable.vue", () => {
|
|
|
480
754
|
await firstRow.trigger("dblclick");
|
|
481
755
|
|
|
482
756
|
expect(component.emitted("doubleClickRow")).toBeTruthy();
|
|
483
|
-
expect(component.emitted("doubleClickRow")![0][0]).
|
|
757
|
+
expect(component.emitted("doubleClickRow")![0][0]).toMatchObject(defaultRows[0]);
|
|
484
758
|
});
|
|
485
759
|
|
|
486
760
|
it("Click Cell – emits clickCell event when cell is clicked", async () => {
|
|
@@ -491,22 +765,25 @@ describe("UTable.vue", () => {
|
|
|
491
765
|
await firstCell.trigger("click");
|
|
492
766
|
|
|
493
767
|
expect(component.emitted("clickCell")).toBeTruthy();
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
]);
|
|
768
|
+
const emittedData = component.emitted("clickCell")![0];
|
|
769
|
+
|
|
770
|
+
expect(emittedData[0]).toBe(defaultRows[0].name);
|
|
771
|
+
expect(emittedData[1]).toMatchObject(defaultRows[0]);
|
|
772
|
+
expect(emittedData[2]).toBe("name");
|
|
499
773
|
});
|
|
500
774
|
|
|
501
775
|
it("Toggle Row Checkbox – emits update:selectedRows when row checkbox is clicked", async () => {
|
|
502
776
|
const component = mountUTable(getDefaultProps({ selectable: true }));
|
|
503
777
|
|
|
504
|
-
const
|
|
778
|
+
const checkboxCell = component.find("tbody tr td[data-checkbox-id]");
|
|
505
779
|
|
|
506
|
-
await
|
|
780
|
+
await checkboxCell.trigger("click");
|
|
507
781
|
|
|
508
782
|
expect(component.emitted("update:selectedRows")).toBeTruthy();
|
|
509
|
-
|
|
783
|
+
const emittedRows = component.emitted("update:selectedRows")![0][0] as Row[];
|
|
784
|
+
|
|
785
|
+
expect(emittedRows).toHaveLength(1);
|
|
786
|
+
expect(emittedRows[0]).toMatchObject(defaultRows[0]);
|
|
510
787
|
});
|
|
511
788
|
|
|
512
789
|
it("Toggle Expand – emits row-expand and update:expandedRows when expand icon is clicked", async () => {
|
|
@@ -525,7 +802,7 @@ describe("UTable.vue", () => {
|
|
|
525
802
|
await expandIcon.trigger("click");
|
|
526
803
|
|
|
527
804
|
expect(component.emitted("row-expand")).toBeTruthy();
|
|
528
|
-
expect(component.emitted("row-expand")![0][0]).
|
|
805
|
+
expect(component.emitted("row-expand")![0][0]).toMatchObject(expandableRow);
|
|
529
806
|
expect(component.emitted("update:expandedRows")).toBeTruthy();
|
|
530
807
|
expect(component.emitted("update:expandedRows")![0][0]).toEqual(["1"]);
|
|
531
808
|
});
|
|
@@ -551,7 +828,7 @@ describe("UTable.vue", () => {
|
|
|
551
828
|
await collapseIcon.trigger("click");
|
|
552
829
|
|
|
553
830
|
expect(component.emitted("row-collapse")).toBeTruthy();
|
|
554
|
-
expect(component.emitted("row-collapse")![0][0]).
|
|
831
|
+
expect(component.emitted("row-collapse")![0][0]).toMatchObject(expandableRow);
|
|
555
832
|
expect(component.emitted("update:expandedRows")).toBeTruthy();
|
|
556
833
|
expect(component.emitted("update:expandedRows")![0][0]).toEqual([]);
|
|
557
834
|
});
|
|
@@ -559,20 +836,33 @@ describe("UTable.vue", () => {
|
|
|
559
836
|
it("Multiple Row Selection – emits update:selectedRows with all selected rows", async () => {
|
|
560
837
|
const component = mountUTable(getDefaultProps({ selectable: true }));
|
|
561
838
|
|
|
562
|
-
const tableRows = component.findAll("tbody tr");
|
|
563
|
-
|
|
564
839
|
// Select first row
|
|
565
|
-
|
|
840
|
+
let tableRows = component.findAll("tbody tr");
|
|
841
|
+
|
|
842
|
+
await tableRows[0].find("td[data-checkbox-id]").trigger("click");
|
|
843
|
+
await nextTick();
|
|
844
|
+
|
|
845
|
+
// Update props with the first selected row
|
|
846
|
+
const firstEmit = component.emitted("update:selectedRows")![0][0] as Row[];
|
|
847
|
+
|
|
848
|
+
await component.setProps({ selectedRows: firstEmit });
|
|
849
|
+
await nextTick();
|
|
850
|
+
|
|
851
|
+
// Re-query the DOM after props update
|
|
852
|
+
tableRows = component.findAll("tbody tr");
|
|
853
|
+
|
|
566
854
|
// Select second row
|
|
567
|
-
await tableRows[1].find("
|
|
855
|
+
await tableRows[1].find("td[data-checkbox-id]").trigger("click");
|
|
856
|
+
await nextTick();
|
|
568
857
|
|
|
569
858
|
const emittedEvents = component.emitted("update:selectedRows");
|
|
570
859
|
|
|
571
860
|
expect(emittedEvents).toBeTruthy();
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
]);
|
|
861
|
+
const emittedRows = emittedEvents![emittedEvents!.length - 1][0] as Row[];
|
|
862
|
+
|
|
863
|
+
expect(emittedRows).toHaveLength(2);
|
|
864
|
+
expect(emittedRows[0]).toMatchObject(defaultRows[0]);
|
|
865
|
+
expect(emittedRows[1]).toMatchObject(defaultRows[1]);
|
|
576
866
|
});
|
|
577
867
|
|
|
578
868
|
it("Nested Row Expansion – emits update:expandedRows for nested rows", async () => {
|
|
@@ -626,5 +916,151 @@ describe("UTable.vue", () => {
|
|
|
626
916
|
expect(component.emitted("clickRow")).toBeFalsy();
|
|
627
917
|
expect(component.emitted("doubleClickRow")).toBeFalsy();
|
|
628
918
|
});
|
|
919
|
+
|
|
920
|
+
it("Search Event – emits when search prop changes", async () => {
|
|
921
|
+
const component = mountUTable(getDefaultProps());
|
|
922
|
+
|
|
923
|
+
await component.setProps({ search: "doe" });
|
|
924
|
+
await nextTick();
|
|
925
|
+
|
|
926
|
+
expect(component.emitted("search")).toBeTruthy();
|
|
927
|
+
expect(component.emitted("search")![0][0]).toBe(1);
|
|
928
|
+
});
|
|
929
|
+
|
|
930
|
+
it("Search Event – emits updated count when search changes", async () => {
|
|
931
|
+
const component = mountUTable(getDefaultProps());
|
|
932
|
+
|
|
933
|
+
await component.setProps({ search: "doe" });
|
|
934
|
+
await nextTick();
|
|
935
|
+
|
|
936
|
+
const initialCount = component.emitted("search")![0][0];
|
|
937
|
+
|
|
938
|
+
await component.setProps({ search: "example" });
|
|
939
|
+
await nextTick();
|
|
940
|
+
|
|
941
|
+
const updatedCount = component.emitted("search")![1][0];
|
|
942
|
+
|
|
943
|
+
expect(updatedCount).toBeGreaterThan(initialCount as number);
|
|
944
|
+
});
|
|
945
|
+
|
|
946
|
+
it("Search Event – emits zero when search is cleared", async () => {
|
|
947
|
+
const component = mountUTable(getDefaultProps());
|
|
948
|
+
|
|
949
|
+
await component.setProps({ search: "doe" });
|
|
950
|
+
await nextTick();
|
|
951
|
+
|
|
952
|
+
await component.setProps({ search: "" });
|
|
953
|
+
await nextTick();
|
|
954
|
+
|
|
955
|
+
const emittedEvents = component.emitted("search");
|
|
956
|
+
|
|
957
|
+
expect(emittedEvents![emittedEvents!.length - 1][0]).toBe(0);
|
|
958
|
+
});
|
|
959
|
+
|
|
960
|
+
it("Search Event – counts all occurrences in a single cell", async () => {
|
|
961
|
+
const rowsWithDuplicates: Row[] = [
|
|
962
|
+
{
|
|
963
|
+
id: "1",
|
|
964
|
+
name: "Test Test Test",
|
|
965
|
+
email: "test@example.com",
|
|
966
|
+
role: "User",
|
|
967
|
+
},
|
|
968
|
+
];
|
|
969
|
+
|
|
970
|
+
const component = mountUTable(getDefaultProps({ rows: rowsWithDuplicates }));
|
|
971
|
+
|
|
972
|
+
await component.setProps({ search: "test" });
|
|
973
|
+
await nextTick();
|
|
974
|
+
|
|
975
|
+
expect(component.emitted("search")![0][0]).toBe(4);
|
|
976
|
+
});
|
|
977
|
+
});
|
|
978
|
+
|
|
979
|
+
describe("Virtual Scroll with Search", () => {
|
|
980
|
+
it("Virtual Scroll with Search – renders only visible rows with search", async () => {
|
|
981
|
+
const manyRows = Array.from({ length: 100 }, (_, i) => ({
|
|
982
|
+
id: String(i + 1),
|
|
983
|
+
name: `User ${i + 1}`,
|
|
984
|
+
email: `user${i + 1}@example.com`,
|
|
985
|
+
role: i % 2 === 0 ? "Admin" : "User",
|
|
986
|
+
}));
|
|
987
|
+
|
|
988
|
+
const component = mountUTable(
|
|
989
|
+
getDefaultProps({
|
|
990
|
+
rows: manyRows,
|
|
991
|
+
virtualScroll: true,
|
|
992
|
+
rowHeight: 40,
|
|
993
|
+
scrollHeight: "400px",
|
|
994
|
+
}),
|
|
995
|
+
);
|
|
996
|
+
|
|
997
|
+
await component.setProps({ search: "Admin" });
|
|
998
|
+
await nextTick();
|
|
999
|
+
|
|
1000
|
+
const tableRows = component.findAllComponents(UTableRow);
|
|
1001
|
+
|
|
1002
|
+
expect(tableRows.length).toBeLessThan(manyRows.length);
|
|
1003
|
+
expect(component.emitted("search")).toBeTruthy();
|
|
1004
|
+
});
|
|
1005
|
+
|
|
1006
|
+
it("Virtual Scroll with Search – passes search match columns to rows", async () => {
|
|
1007
|
+
const component = mountUTable(
|
|
1008
|
+
getDefaultProps({
|
|
1009
|
+
virtualScroll: true,
|
|
1010
|
+
search: "john",
|
|
1011
|
+
}),
|
|
1012
|
+
);
|
|
1013
|
+
|
|
1014
|
+
await nextTick();
|
|
1015
|
+
|
|
1016
|
+
const tableRow = component.getComponent(UTableRow);
|
|
1017
|
+
|
|
1018
|
+
expect(tableRow.props("search")).toBe("john");
|
|
1019
|
+
expect(tableRow.props("searchMatchColumns")).toBeDefined();
|
|
1020
|
+
});
|
|
1021
|
+
|
|
1022
|
+
it("Virtual Scroll with Search – handles searchMatch prop", async () => {
|
|
1023
|
+
const manyRows = Array.from({ length: 50 }, (_, i) => ({
|
|
1024
|
+
id: String(i + 1),
|
|
1025
|
+
name: `User ${i + 1}`,
|
|
1026
|
+
email: `user${i + 1}@example.com`,
|
|
1027
|
+
role: "User",
|
|
1028
|
+
}));
|
|
1029
|
+
|
|
1030
|
+
const component = mountUTable(
|
|
1031
|
+
getDefaultProps({
|
|
1032
|
+
rows: manyRows,
|
|
1033
|
+
virtualScroll: true,
|
|
1034
|
+
search: "user",
|
|
1035
|
+
searchMatch: 0,
|
|
1036
|
+
}),
|
|
1037
|
+
);
|
|
1038
|
+
|
|
1039
|
+
await nextTick();
|
|
1040
|
+
|
|
1041
|
+
expect(component.vm.$props.searchMatch).toBe(0);
|
|
1042
|
+
});
|
|
1043
|
+
|
|
1044
|
+
it("Virtual Scroll with Search – emits search event with virtual scroll enabled", async () => {
|
|
1045
|
+
const manyRows = Array.from({ length: 100 }, (_, i) => ({
|
|
1046
|
+
id: String(i + 1),
|
|
1047
|
+
name: `User ${i + 1}`,
|
|
1048
|
+
email: `user${i + 1}@example.com`,
|
|
1049
|
+
role: "User",
|
|
1050
|
+
}));
|
|
1051
|
+
|
|
1052
|
+
const component = mountUTable(
|
|
1053
|
+
getDefaultProps({
|
|
1054
|
+
rows: manyRows,
|
|
1055
|
+
virtualScroll: true,
|
|
1056
|
+
}),
|
|
1057
|
+
);
|
|
1058
|
+
|
|
1059
|
+
await component.setProps({ search: "user" });
|
|
1060
|
+
await nextTick();
|
|
1061
|
+
|
|
1062
|
+
expect(component.emitted("search")).toBeTruthy();
|
|
1063
|
+
expect(component.emitted("search")![0][0]).toBeGreaterThan(0);
|
|
1064
|
+
});
|
|
629
1065
|
});
|
|
630
1066
|
});
|