Fragmented Thought

Exposing custom controls using Storybook 7's new Meta type

By

Published:

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: https://storybook.js.org/docs/7.0/react/writing-docs/docs-page tags: ["autodocs"], parameters: { // More on Story layout: https://storybook.js.org/docs/react/configure/story-layout layout: "centered", }, // More on argTypes: https://storybook.js.org/docs/react/api/argtypes argTypes: {}, args: { // Include our custom args title, subtitle, // And args for the component content: ` # Heading 1 ${oneLoremIpsumParagraph} - ${oneLoremIpsumSentence} - ${oneLoremIpsumSentence} - ${oneLoremIpsumSentence} ![img](https://i0.wp.com/css-tricks.com/wp-content/uploads/2016/01/choose-markdown.jpg) [Learn markdown](https://www.markdownguide.org/) `.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: https://storybook.js.org/docs/react/writing-stories/introduction#using-args export const Primary: Story = { // More on args: https://storybook.js.org/docs/react/writing-stories/args args: {}, };