|
1 | 1 | import { render, screen } from "@testing-library/svelte"; |
2 | 2 | import type { ComponentProps } from "svelte"; |
| 3 | +import { expectTypeOf } from "vitest"; |
3 | 4 | import { user } from "../setup-tests"; |
4 | 5 | import ComboBox from "./ComboBox.test.svelte"; |
5 | 6 | import ComboBoxCustom from "./ComboBoxCustom.test.svelte"; |
@@ -38,7 +39,7 @@ describe("ComboBox", () => { |
38 | 39 |
|
39 | 40 | expect(consoleLog).toHaveBeenCalledWith("select", { |
40 | 41 | selectedId: "1", |
41 | | - selectedItem: { id: "1", text: "Email" }, |
| 42 | + selectedItem: { id: "1", text: "Email", price: 200 }, |
42 | 43 | }); |
43 | 44 | expect(getInput()).toHaveValue("Email"); |
44 | 45 | }); |
@@ -524,4 +525,105 @@ describe("ComboBox", () => { |
524 | 525 | await user.click(document.body); |
525 | 526 | expect(input).toHaveValue("Email"); |
526 | 527 | }); |
| 528 | + |
| 529 | + describe("Generics", () => { |
| 530 | + it("should support custom item types with generics", () => { |
| 531 | + type Product = { |
| 532 | + id: string; |
| 533 | + text: string; |
| 534 | + price: number; |
| 535 | + category: string; |
| 536 | + inStock: boolean; |
| 537 | + }; |
| 538 | + |
| 539 | + const products: Product[] = [ |
| 540 | + { |
| 541 | + id: "1", |
| 542 | + text: "Laptop", |
| 543 | + price: 999, |
| 544 | + category: "Electronics", |
| 545 | + inStock: true, |
| 546 | + }, |
| 547 | + { |
| 548 | + id: "2", |
| 549 | + text: "Phone", |
| 550 | + price: 599, |
| 551 | + category: "Electronics", |
| 552 | + inStock: false, |
| 553 | + }, |
| 554 | + ]; |
| 555 | + |
| 556 | + expectTypeOf<typeof products>().toEqualTypeOf<Product[]>(); |
| 557 | + |
| 558 | + const itemToString = (item: Product) => `${item.text} - $${item.price}`; |
| 559 | + expectTypeOf(itemToString).parameter(0).toEqualTypeOf<Product>(); |
| 560 | + expectTypeOf(itemToString).returns.toEqualTypeOf<string>(); |
| 561 | + |
| 562 | + const shouldFilterItem = (item: Product, value: string) => |
| 563 | + item.category.toLowerCase().includes(value.toLowerCase()) || |
| 564 | + item.text.toLowerCase().includes(value.toLowerCase()); |
| 565 | + expectTypeOf(shouldFilterItem).parameter(0).toEqualTypeOf<Product>(); |
| 566 | + expectTypeOf(shouldFilterItem).parameter(1).toEqualTypeOf<string>(); |
| 567 | + expectTypeOf(shouldFilterItem).returns.toEqualTypeOf<boolean>(); |
| 568 | + |
| 569 | + type SelectEvent = CustomEvent<{ |
| 570 | + selectedId: string; |
| 571 | + selectedItem: Product; |
| 572 | + }>; |
| 573 | + expectTypeOf< |
| 574 | + SelectEvent["detail"]["selectedItem"] |
| 575 | + >().toEqualTypeOf<Product>(); |
| 576 | + }); |
| 577 | + |
| 578 | + it("should provide type-safe access to custom properties in callbacks", () => { |
| 579 | + type Tag = { |
| 580 | + id: number; |
| 581 | + text: string; |
| 582 | + color: string; |
| 583 | + usageCount: number; |
| 584 | + }; |
| 585 | + |
| 586 | + // itemToString can access custom properties |
| 587 | + const itemToString = (item: Tag) => { |
| 588 | + expectTypeOf(item).toHaveProperty("color"); |
| 589 | + expectTypeOf(item).toHaveProperty("usageCount"); |
| 590 | + return `${item.text} (${item.usageCount})`; |
| 591 | + }; |
| 592 | + |
| 593 | + // shouldFilterItem can access custom properties |
| 594 | + const shouldFilterItem = (item: Tag, value: string) => { |
| 595 | + expectTypeOf(item).toHaveProperty("color"); |
| 596 | + expectTypeOf(item).toHaveProperty("usageCount"); |
| 597 | + return ( |
| 598 | + item.color.includes(value) || item.usageCount > parseInt(value, 10) |
| 599 | + ); |
| 600 | + }; |
| 601 | + |
| 602 | + expectTypeOf(itemToString).parameter(0).toEqualTypeOf<Tag>(); |
| 603 | + expectTypeOf(shouldFilterItem).parameter(0).toEqualTypeOf<Tag>(); |
| 604 | + }); |
| 605 | + |
| 606 | + it("should support slot props with generic item type", () => { |
| 607 | + type MenuItem = { |
| 608 | + id: string; |
| 609 | + text: string; |
| 610 | + icon: string; |
| 611 | + shortcut?: string; |
| 612 | + }; |
| 613 | + |
| 614 | + type SlotProps = { item: MenuItem; index: number }; |
| 615 | + |
| 616 | + const slotItem: MenuItem = { |
| 617 | + id: "1", |
| 618 | + text: "Save", |
| 619 | + icon: "save-icon", |
| 620 | + shortcut: "Ctrl+S", |
| 621 | + }; |
| 622 | + |
| 623 | + // Slot should provide item with custom properties |
| 624 | + expectTypeOf<SlotProps["item"]>().toEqualTypeOf<MenuItem>(); |
| 625 | + expectTypeOf(slotItem).toHaveProperty("icon"); |
| 626 | + expectTypeOf(slotItem).toHaveProperty("shortcut"); |
| 627 | + }); |
| 628 | + }); |
527 | 629 | }); |
0 commit comments