S
SolidJS•3mo ago
karolisk

Issues when using SolidJS with child web components

Hi, I have problems passing properties to web components that is wrapped in certain web component (modus-toolbar in this case). According to docs I should use "prop:*" but that doesn't compile. It works when I use "attr:*", but this breaks intellisense. And I'm confused because when I hardcode string it works without any prefixes. I create stackblitz example: https://stackblitz.com/edit/solidjs-templates-7qjaxe?file=src%2FApp.tsx Thanks for looking into this 🙂
4 Replies
karolisk
karoliskOP•3mo ago
Here is main code with issues:
<modus-toolbar>
{/* if I remove modus-toolbar, everything works */}
<For each={props.menuItems}>
{(section, idx) => (
<>
<For each={section}>
{(item) => (
<>
{/* doesn't work? */}
<modus-tooltip text={item.name} position="right">
<modus-button icon-only={item.name}></modus-button>
</modus-tooltip>
{/* works, but intellisense is broken */}
<modus-tooltip attr:text={item.name} position="right">
<modus-button attr:icon-only={item.name}></modus-button>
</modus-tooltip>
{/* prop: doesn't compile at all??
<modus-tooltip prop:text={item.name} position="right">
<modus-button prop:icon-only={item.name}></modus-button>
</modus-tooltip>
*/}
{/* works... */}
<modus-tooltip text="arrow_next" position="right">
<modus-button icon-only="arrow_next"></modus-button>
</modus-tooltip>
{/* doesn't work? */}
<modus-tooltip
text={props.menuItems[0][0].name}
position="right"
>
<modus-button
icon-only={props.menuItems[0][0].name}
></modus-button>
</modus-tooltip>
</>
)}
</For>
<Show when={idx() !== props.menuItems.length - 1}>
<modus-divider></modus-divider>
</Show>
</>
)}
</For>
</modus-toolbar>
<modus-toolbar>
{/* if I remove modus-toolbar, everything works */}
<For each={props.menuItems}>
{(section, idx) => (
<>
<For each={section}>
{(item) => (
<>
{/* doesn't work? */}
<modus-tooltip text={item.name} position="right">
<modus-button icon-only={item.name}></modus-button>
</modus-tooltip>
{/* works, but intellisense is broken */}
<modus-tooltip attr:text={item.name} position="right">
<modus-button attr:icon-only={item.name}></modus-button>
</modus-tooltip>
{/* prop: doesn't compile at all??
<modus-tooltip prop:text={item.name} position="right">
<modus-button prop:icon-only={item.name}></modus-button>
</modus-tooltip>
*/}
{/* works... */}
<modus-tooltip text="arrow_next" position="right">
<modus-button icon-only="arrow_next"></modus-button>
</modus-tooltip>
{/* doesn't work? */}
<modus-tooltip
text={props.menuItems[0][0].name}
position="right"
>
<modus-button
icon-only={props.menuItems[0][0].name}
></modus-button>
</modus-tooltip>
</>
)}
</For>
<Show when={idx() !== props.menuItems.length - 1}>
<modus-divider></modus-divider>
</Show>
</>
)}
</For>
</modus-toolbar>
karolisk
karoliskOP•3mo ago
GitHub
modus-web-components/stencil-workspace/src/components/modus-toolbar...
This library provides Modus components as web components - reusable, encapsulated UI elements that are framework agnostic (can be implemented in any site). - trimble-oss/modus-web-components
Dakotys
Dakotys•3mo ago
@karolisk Things that are happening: modus-card has static values so they are treated as attributes, In the first tooltip text is dynamic so he is treated as a prop, position is static - attr; no errors because they are defined in ts, icon-only is not defined in ts only iconOnly but solid allows hyphenated values like data-icon without ts errors but doesnt check if they are defined as types and also is dynamic - prop so it ends up back in kebabCase as el.iconOnly = value , In second there is ts error because attr:value is not defined, but it works because dynamic values are forced to being attr and position is coincidentally static. In third icon-only is hyphenated value can be only attr because if it is a prop it becomes el.some-value = value that throws js error. In forth its all static - attr - works, intelisens autocomplete works except icon-only because in ModusJSX its defined as iconOnly. in fifth text- dyn-prop, position-static-attr, icon-only - dynamic - prop- el.iconOnly = value - doesnt work. Why modus-alert works with prop:message and just message, both get converted into el.message = value and attr:message el.setAttribute("mesage", value), probably for some reason dev extracts attribute and then for better handling it sets directly as a prop so in both cases it ends up as a prop so it works either way, just a guess. The best solution is to force everything to be attr so we don't have to distinguish between dyn and static values. Change intristic.d.ts to
import { JSX as ModusJSX } from '@trimble-oss/modus-web-components';
import { JSX } from 'solid-js';

type ToKebabCase<S extends string> = S extends `${infer T}${infer U}`
? `${T extends Lowercase<T> ? '' : '-'}${Lowercase<T>}${ToKebabCase<U>}`
: S;

// Utility to convert element attributes to "attr:kebab-case" format
type ConvertAttributes<T> = {
[Key in keyof T as `attr:${ToKebabCase<string & Key>}`]: T[Key];
};

// Main type for ModusCustomElements
declare type ModusCustomElements = {
[Element in keyof ModusJSX.IntrinsicElements]: ConvertAttributes<
ModusJSX.IntrinsicElements[Element]
> & {
children?: JSX.Element;
};
};

declare module 'solid-js' {
namespace JSX {
interface IntrinsicElements extends ModusCustomElements {}
}
}

import { JSX as ModusJSX } from '@trimble-oss/modus-web-components';
import { JSX } from 'solid-js';

type ToKebabCase<S extends string> = S extends `${infer T}${infer U}`
? `${T extends Lowercase<T> ? '' : '-'}${Lowercase<T>}${ToKebabCase<U>}`
: S;

// Utility to convert element attributes to "attr:kebab-case" format
type ConvertAttributes<T> = {
[Key in keyof T as `attr:${ToKebabCase<string & Key>}`]: T[Key];
};

// Main type for ModusCustomElements
declare type ModusCustomElements = {
[Element in keyof ModusJSX.IntrinsicElements]: ConvertAttributes<
ModusJSX.IntrinsicElements[Element]
> & {
children?: JSX.Element;
};
};

declare module 'solid-js' {
namespace JSX {
interface IntrinsicElements extends ModusCustomElements {}
}
}

And then you will be forced to use attr:height and so on everywhere, you also have intellisense so you get suggestions for attr:position like 'right' and so, and you dont have to worry about static and dyn
karolisk
karoliskOP•3mo ago
@Dakotys wow, that's a great explanation, I learned a lot! And I like you solution. I thought adding static strings is not possible in typescript types.
Want results from more Discord servers?
Add your server