themesberg / flowbite-svelte

Official Svelte components built for Flowbite and Tailwind CSS
https://flowbite-svelte.com
MIT License
2.13k stars 260 forks source link

Listgroup typing for slot use case #1159

Open MarcLoupias opened 10 months ago

MarcLoupias commented 10 months ago

Summary

Similar issue to the items one.

There is 2 typings issues :

I cannot pass down the index props from the {#each items as item, index} : svelte-check output this error : Error: Property 'index' does not exist on type '{ item: string | ListGroupItemType; }'. (ts).

And for the second problem when i pass a custom type to my custom component i got a typing error for {item} in the loop because item type is limited to string or ListgroupItem : Error: Type 'string | ListGroupItemType' is not assignable to type 'CustomViewModel'. Type 'string' is not assignable to type 'CustomViewModel'. (ts)

Should be crystal clear with basic example and motivation, see below.

Basic example

<script lang="ts">
    import { Listgroup } from 'flowbite-svelte';
    import CustomComponent from './CustomComponent.svelte';

    let links = [
        { name: "Accordions", href: "/accordions", current: true },
        { name: "Alerts", href: "/alerts" },
        { name: "Badges", href: "/badges"  },
        { name: "Breadcrumbs", href: "/breadcrumbs" },
    ];
</script>

<Listgroup items={links} let:item let:index class="w-48">
    <CustomComponent data="{item}" index={index}></CustomComponent>
</Listgroup>

Motivation

Using the list index in a custom component inside the Listgroup is very useful for integration or e2e testing with tools like Cypress or Playwright to generate ids at runtime to allow consistent selectors writing easily.

Let's say your CustomComponent.svelte is something like that :

<script lang="ts">
    import { type CustomViewModel } from './xxxx'

    export let data: CustomViewModel;
    export let index: number;
</script>

<div id={'custom-component-id-' + index.toString()}>
    <!-- CustomComponent content -->
</div>

Motivation for using a custom type for a CustomComponent is obvious, it is custom so the contract between the component and the caller cannot be limited to string or ListGroupItemType, it have to be custom ;-) The contract is also the view model as theses components inside a loop are mostly dumb components.

Notice that this is only a TypeScript issue, the behavior at runtime is OK.

MarcLoupias commented 10 months ago

Notice that this problem is really annoying as there is no solution to disable a TS error in a svelte file.

I have created this github issue as a feature request because it is not something implemented that is not working as expected but something that is not implemented at all yet.

jjagielka commented 9 months ago

Just issued a PR addressing the type of index in each loop - please check.

As for ListgroupItemType:

To address your CustomComponent issue I'd suggest that instead of:

<Listgroup items={links} let:item let:index class="w-48">
    <CustomComponent data="{item}" index={index}></CustomComponent>
</Listgroup>

you do the individual props in your CustomComponent so you can call it like:

<Listgroup items={links} let:item let:index class="w-48">
    <CustomComponent name={item.name} href={item.href} index={index}></CustomComponent>
</Listgroup>
dusty-phillips commented 6 months ago

FWIW, I'm getting a "Property 'name' does not exist on type 'string' error" even with the latter syntax. I can't see any way to make the buttons example from the docs type safe:

Screenshot 2024-03-04 at 1 43 29 PM
Code for Copy Paste ```svelte alert(Object.entries(e.detail))}> {item.name} ```