Using SolidJS, how can I properly import the official Material 3 Web Components? e.g. https://github.com/material-components/material-web/blob/main/docs/components/tabs.md
My main issue so far has been typescript errors, I can see the components in the page itself.
I've imported the required js files like so in my index.tsx
:
import '@material/web/button/filled-button.js';
import '@material/web/button/outlined-button.js';
import '@material/web/checkbox/checkbox.js';
import '@material/web/tabs/tabs.js';
import '@material/web/tabs/secondary-tab.js';
import '@material/web/tabs/primary-tab.js';
The problem is that I get type errors for each Web Component instance:
<md-tabs>
<md-primary-tab>Birds</md-primary-tab>
<md-secondary-tab>Cats</md-secondary-tab>
<md-secondary-tab>Dogs</md-secondary-tab>
</md-tabs>
I know I could kind of work around it by doing something like the following, but that's using any
which isn't really a good solution.
declare module "solid-js" {
namespace JSX {
interface IntrinsicElements {
"md-tabs": any
}
}
}
Material 3 Web Components ship with .d.ts
files, but I'm not sure how to make SolidJS aware of those declarations, I've tried /// <reference types="@material/web/all" />
to no avail.
I've also tried the following:
import { MdTabs } from "@material/web/tabs/tabs.js";
import { MdPrimaryTab } from "@material/web/tabs/primary-tab";
import { MdSecondaryTab } from "@material/web/tabs/secondary-tab.js";
declare module "solid-js" {
namespace JSX {
interface IntrinsicElements {
"md-tabs": MdTabs,
'md-primary-tab': MdPrimaryTab,
'md-secondary-tab': MdSecondaryTab
}
}
}
But then it still shows an error for md-primary-tab
and md-secondary-tab
(no error for md-tabs
though).
Type 'import("c:/Users/USER/dev/sstart/node_modules/solid-js/types/jsx").JSX.Element' is not assignable to type 'Element'.
Type 'undefined' is not assignable to type 'Element'.ts(2322)
This JSX tag's 'children' prop expects type 'HTMLCollection' which requires multiple children, but only a single child was provided.ts(2745)
A workaround is to do the following, but it's still feels pretty hacky:
declare module "solid-js" {
namespace JSX {
type MarriedWithChildren = {children:any} | {} | {class?:string, classList?:Record<string,boolean>
interface IntrinsicElements {
"md-tabs": MdTabs | MarriedWithChildren,
'md-primary-tab': MdPrimaryTab | MarriedWithChildren,
'md-secondary-tab': MdSecondaryTab | MarriedWithChildren
}
}
}
After some playing around, I ended up doing the following in global.d.ts
import "@material/web/all"
type WithAnyChildrenAndClasses = {children:any} | {} | {class?:string, classList?:Record<string,boolean>};
type SolidInterface = {
[P in keyof HTMLElementTagNameMap]: HTMLElementTagNameMap[P] | WithAnyChildrenAndClasses;
};
declare module "solid-js" {
namespace JSX {
interface IntrinsicElements extends SolidInterface { }
}
}
A better solution has been suggested here:
https://github.com/shoelace-style/shoelace/discussions/770#discussioncomment-2852125
declare module 'solid-js' {
namespace JSX {
type ElementProps<T> = {
// Add both the element's prefixed properties and the attributes
[K in keyof T]: Props<T[K]> & HTMLAttributes<T[K]>;
}
// Prefixes all properties with `prop:` to match Solid's property setting syntax
type Props<T> = {
[K in keyof T as `prop:${string & K}`]?: T[K];
}
interface IntrinsicElements extends ElementProps<HTMLElementTagNameMap> {
}
}
}
PS
To use the classList syntax, with tailwind autocompletion such as
<md-button classList={{"bg-blue-500":someSignal()}}></md-button>
add the following in vscode/.settings.json
{
"tailwindCSS.classAttributes": ["class", "classList", "className"]
}