I'm trying to create a utility component for displaying typography and it needs to be semantic (not just a div). I want to limit the choices for the tags to be either an h1, h2, h3, or p tag only.
I've tried the following code, where if body1 or body2 is passed, it will be mapped to a <p> tag:
type Props = {
children: string;
variant?: 'h1' | 'h2' | 'h3' | 'body1' | 'body2';
}
export default function Typography({children, variant}: Props) {
if (variant === 'body1' || variant === 'body2') return <p>{children}</p>
const Tag = variant || 'p';
return <Tag>{children}</Tag>
}
But when I use this component, it does not allow me to pass the underlying html attributes such as className etc. So I read about polymorphism in typescript and has seen this code in one of the tutorials:
import { ComponentProps, ElementType, ReactNode } from "react";
type TextOwnProps<E extends ElementType> = {
size?: "sm" | "md" | "lg";
color?: "primary" | "secondary";
children: ReactNode;
as?: E;
};
type TextProps<E extends ElementType> = TextOwnProps<E> & Omit<ComponentProps<E>, keyof TextOwnProps<E>>;
export const Text = <E extends ElementType = "div">({
size,
color,
children,
as,
}: TextProps<E>) => {
const Component = as || "div";
return <Component className={`class-with-${size}-${color}`}>{children}</Component>;
};
This makes it possible to pass html attributes (and removes conflict from custom props). However, I do not know how to limit this to only the values I want. The as prop displays all valid html elements.
React.js not support this syntax in JSX, but you can use switch statements and check tag name. Edited: React.createElement(as, props, children) https://ru.reactjs.org/docs/react-without-jsx.html