Components With Props
Faster, smoother development and reusability with components and props.
Our site is currently one "component" which serves as an 11ty page/template. We don't really have reuse nor the
traditional concept of components with props. In this step, we break out part of our page into a Heading
component
which can be used on any page, can accept "props", and can be developed in isolation.
Let's say our site content will be in site
and our software components in components
:
Our vitest.config.js
file needs to be pointed at both the site
and components
directories:
import { defineConfig } from "vitest/config";
export default defineConfig({
esbuild: {
jsx: "transform",
jsxInject: "import { jsx } from 'jsx-async-runtime/jsx-runtime'",
jsxFactory: "jsx",
jsxImportSource: "jsx-async-runtime",
},
test: {
environment: "happy-dom",
include: ["./components/**/*.test.tsx", "./site/**/*.test.tsx"],
},
});
We should do something similar in tsconfig.json
:
{
"compilerOptions": {
"module": "ESNext",
"target": "ESNext",
"moduleResolution": "Node",
"skipLibCheck": true,
"jsx": "react-jsx",
"jsxImportSource": "jsx-async-runtime"
},
"include": ["components", "site"],
"exclude": ["node_modules", "_site"]
}
With this in place, let's make components/Heading.tsx
(note that it didn't need the .11ty.
in the filename):
export type HeadingProps = {
name?: string;
};
export function Heading({ name = "TSX" }: HeadingProps): JSX.Element {
return <h1>Hello {name}</h1>;
}
We can test the default and passed-in-value cases in components/Heading.test.tsx
:
import { expect, test } from "vitest";
import { renderToString } from "jsx-async-runtime";
import { screen } from "@testing-library/dom";
import { Heading } from "./Heading";
test("render heading with default name", async () => {
const result = <Heading />;
document.body.innerHTML = await renderToString(result);
expect(screen.getByText("Hello TSX")).toBeTruthy();
});
test("render heading with custom name", async () => {
const result = <Heading name={`World`} />;
document.body.innerHTML = await renderToString(result);
expect(screen.getByText("Hello World")).toBeTruthy();
});
Running this in the IDE shows that our component works in isolation -- using a fake browser (Happy-DOM):
In fact, you can go into true TDD mode and run Vitest in watch mode. It's so fast, you get answers as you type. No need to re-run your site and revisit your browser.
Now, let's go back to our site/index.11ty.tsx
template and point it at a component:
import { Heading } from "../components/Heading";
export function Index(): JSX.Element {
return <Heading />;
}
export const render = Index;
Tests all pass, the page still builds the same. All good.
In the next step, we'll take our component and make it even more testable.