Fragmented Thought

Exposing custom controls using Storybook 7's new Meta type



Lance Gliser

By default, all stories using the Component Story Format sense and provide the component's details through props. Yet there's times when you need a way to customize a story doing something that isn't a part of the component itself. Most often this comes up when you're using the render method, and need to test out variants of a wrapping element.

In this example, we'll wrap an arbitrary Markdown component in MUI's cards and give a means of setting the card header and summary text to the controls.

import Markdown from "./Markdown"; import React from "react"; import { Card, CardHeader, CardContent, Typography } from "@mui/material"; const oneLoremIpsum = getLoremIpsumParagraphs(1); const title = oneLoremIpsum.split(" ").slice(0, 4).join(" "); const subtitle = oneLoremIpsum.split(" ").slice(0, 9).join(" "); // Create a type to declare our custom control additions: type ControlArgs = { title: string; subtitle: string; }; // We'll bypass the standard Meta type: // const meta: Meta<typeof Markdown> = { ... } // Instead using the types directly: const meta: ComponentAnnotations< ReactRenderer, ComponentProps<typeof Markdown> & ControlArgs > = { component: Markdown, // This component will have an automatically generated Autodocs entry: tags: ["autodocs"], parameters: { // More on Story layout: layout: "centered", }, // More on argTypes: argTypes: {}, args: { // Include our custom args title, subtitle, // And args for the component content: ` # Heading 1 ${oneLoremIpsumParagraph} - ${oneLoremIpsumSentence} - ${oneLoremIpsumSentence} - ${oneLoremIpsumSentence} ![img]( [Learn markdown]( `.trim(), }, // Pull our custom args out for use, and continue to pass the rest along. render: ({ title, subtitle, ...args }) => ( <Card> <CardHeader title={title} /> <CardContent sx={{ width: "65em" }}> <Typography>{subtitle}</Typography> <Markdown {...args} /> </CardContent> </Card> ), }; export default meta; type Story = StoryObj<typeof meta>; // More on component templates: export const Primary: Story = { // More on args: args: {}, };