ui icon indicating copy to clipboard operation
ui copied to clipboard

feat: ✨ add storybook to cli

Open lloydrichards opened this issue 2 years ago • 64 comments

Goals/Scope

This is extension of PR #11 regarding adding stories for storybook through the CLI. A lot of work was already done by XavierGeerinck to get this going, but the PR seems to have been lost despite renewed interest and push to get this feature added. I've created this renewed PR with the hope of getting some attention and momentum in adding this to the CLI.

For the sake of narrowing the scope of this PR, i've removed the typography component so we can focus on just storybook.

Description

There are two main areas that have been changed in this PR, first is the addition of a storybook folder in the /registry which contain *.story.tsx files for most of the component (some are still missing). Second is I've added an additional option to the component.tsx which should allow for the stories to be loaded in through the CLI when adding a component (still need to test)

Screenshot 2024-02-13 at 20 18 41

Component Stories

The stories are added to the registry folder in the *.story.tsx files. While most of the components follow the examples in the shadcn/ui docs, some of the examples needed to be simplified or broken up into multiple stories to make them more readable. Below you will find a list of the components and their stories.

Expand Component Table
Checkbox Name Link to Storybook
accordion Storybook
alert Storybook
alert-dialog Storybook
aspect-ratio Storybook
avatar Storybook
badge Storybook
breadcrumb Storybook
button Storybook
calendar Storybook
card Storybook
carousel Storybook
checkbox Storybook
collapsible Storybook
command Storybook
context-menu Storybook
dialog Storybook
drawer Storybook
dropdown-menu Storybook
form Storybook
hover-card Storybook
input-otp Storybook
input Storybook
label Storybook
menubar Storybook
navigation-menu Storybook
pagination Storybook
popover Storybook
progress Storybook
radio-group Storybook
resizable Storybook
scroll-area Storybook
select Storybook
separator Storybook
sheet Storybook
skeleton Storybook
slider Storybook
sonner Storybook
switch Storybook
table Storybook
tabs Storybook
textarea Storybook
toast Storybook
toggle Storybook
toggle-group Storybook
tooltip Storybook
Bonus Name Link to Stories
⭐️ color - functional Storybook
⭐️ color - tailwind Storybook
⭐️ radius Storybook
⭐️ shadow Storybook
⭐️ padding Storybook
⭐️ typography - font family Storybook
⭐️ typography - font size Storybook
⭐️ typography - font weight Storybook

Comments

I'm very happy for support with getting this PR through so if anyone would like to review the stories, or add new ones, or help with upgrading the CLI, please join the conversation in the comments 🙇

if you would like to add a missing story, you can create the .story.tsx file in the /apps/www/registry/stories/ directory and add the file to the apps/www/public/registry/index.json for the correct component.


  • [x] cc: @XavierGeerinck @Integrayshaun

lloydrichards avatar Sep 19 '23 20:09 lloydrichards

@lloydrichards is attempting to deploy a commit to the shadcn-pro Team on Vercel.

A member of the Team first needs to authorize it.

vercel[bot] avatar Sep 19 '23 20:09 vercel[bot]

I believe!!!

csartor avatar Sep 22 '23 14:09 csartor

@lloydrichards did you have any trouble with the code blocks showing corrupted code? Happens to me with <Source/> and just the auto docs image

Jared-Dahlke avatar Sep 27 '23 15:09 Jared-Dahlke

@lloydrichards did you have any trouble with the code blocks showing corrupted code? Happens to me with <Source/> and just the auto docs

I had a look and things loook correct in the code block for my dev env, but i've gone and upgraded storybook to v7.4.5 incase there was some bug in there somewhere. are you using pnpm storybook to spin it up?

lloydrichards avatar Sep 28 '23 11:09 lloydrichards

@lloydrichards did you have any trouble with the code blocks showing corrupted code? Happens to me with <Source/> and just the auto docs

I had a look and things loook correct in the code block for my dev env, but i've gone and upgraded storybook to v7.4.5 incase there was some bug in there somewhere. are you using pnpm storybook to spin it up?

i implemented a lot of stories myself before i found your repo. Now i cloned yours and am running it and got this error:

image

just fyi. Seems like awesome work though. I've also built tests for about half of the components so far. i will have to write tests for all of them soon

Jared-Dahlke avatar Sep 28 '23 15:09 Jared-Dahlke

i implemented a lot of stories myself before i found your repo. Now i cloned yours and am running it and got this error:

I think the issue is using npm vs pnpm. i usually use yarn and was running into issues with yarn install, but noticed the pnpm.lock so tried using pnpm install instead.

lloydrichards avatar Sep 28 '23 15:09 lloydrichards

ahhh nevermind, i was on main, i needed to git checkout feat/storybook-cli

now it works

Jared-Dahlke avatar Sep 28 '23 15:09 Jared-Dahlke

@lloydrichards what do you think is the easiest way to add the import code block on each story at the top. like this:

import { Button } from 'mylibrary/button'`

Right now i'm having to make a .mdx for each story and put that at the top manually like this:

image

Jared-Dahlke avatar Sep 28 '23 15:09 Jared-Dahlke

The way its done through the CLI, is that within the /registry/ui components they reference using aliases:

import { buttonVariants } from "@/registry/default/ui/button"

and then later in the CLI process there is a pretty cool transform-import function (/packages/cli/src/utils/transformers/transform-import.ts) that swaps out the @/registry/default with the config from component.ts

This should also apply to the .stories.tsx files so if you use the @/registry/default alias for importing then the CLI process will fix it to your mylibrary/

lloydrichards avatar Sep 28 '23 15:09 lloydrichards

@lloydrichards This looks great!!! 😁 is there anything that you need from me or the Storybook team to wrap up?

ShaunEvening avatar Sep 29 '23 13:09 ShaunEvening

is there anything that you need from me or the Storybook team to wrap up?

One piece of the puzzle that is still missing is bring able to infer the arg types better for the components. Normally in a story the Meta<typeof Component> does a pretty good job building out the controls, but i've noticed with the Radix components that this only works if you specify a prop in the Story.args. Do you know if there is a better way to infer the argTypes, otherwise its a manual process of picking which props are most useful for each story

lloydrichards avatar Sep 29 '23 15:09 lloydrichards

@shadcn Would it be possible to have a look and see if the changes im proposing here to the CLI and the inclusion o Storybook is inline with you roadmap with shadch/ui? So far I've achieved the core of what i want to do with this PR:

  • [x] write storybook files for all ui components
  • [x] integrate the stories into the CLI so they can be loaded when added
  • [x] keep the PR up to date with the latest main and storybook versions

next steps for me really depend if there is any pushback from your side or any requests for changes. just let me know or if you don't have time, maybe ping another maintainer so I can try test the CLI? 🙏

lloydrichards avatar Oct 01 '23 20:10 lloydrichards

One piece of the puzzle that is still missing is bring able to infer the arg types better for the components. Normally in a story the Meta does a pretty good job building out the controls, but i've noticed with the Radix components that this only works if you specify a prop in the Story.args. Do you know if there is a better way to infer the argTypes, otherwise its a manual process of picking which props are most useful for each story

@lloydrichards —

This is most likely because Storybook, by default, filters out all props sourced from node_modules (otherwise, you end up with every prop from, say, @types/react, which includes all valid HTML attributes).

Try adjusting the prop filter like so:

// .storybook/main.ts
import type { StorybookConfig } from "@storybook/nextjs"

const config: StorybookConfig = {
  // ...
  typescript: {
    reactDocgen: 'react-docgen-typescript',
    reactDocgenTypescriptOptions: {
      shouldExtractLiteralValuesFromEnum: true,
      // 👇 Adjusted prop filter
      propFilter: (prop) =>
        prop.parent ? !/node_modules\/(?!@radix-ui)/.test(prop.parent.fileName) : true,
    },
  },
}
export default config

kylegach avatar Oct 02 '23 20:10 kylegach

@lloydrichards This is incredible work.

I'd like to take a closer look at this (mostly to understand what maintenance effort this will require).

I'll add this to my list and get back to it early next week.

shadcn avatar Oct 03 '23 17:10 shadcn

@shadcn If you have any questions for the Storybook team or ever need a hand, we're always around

ShaunEvening avatar Oct 04 '23 13:10 ShaunEvening

This is awesome @lloydrichards . I'm already using your stories in my shadcn npm library.

not to muddy the waters, but i have implemented unit tests on all of these stories, using Storybook stories. For example here is the Tooltip test:

// tooltip.spec.tsx

import { render, screen, waitFor } from "@testing-library/react";

import { composeStory } from "@storybook/react";

import Meta, { Base } from "./tooltip.stories"; //👈 Our stories imported here.
import userEvent from "@testing-library/user-event";

const BaseStory = composeStory(Base, Meta);

test("Checks if the text displays after hovering", async () => {
  render(<BaseStory />);

  const buttonElement = screen.getByRole("button");
  await userEvent.hover(buttonElement);
  await waitFor(() => screen.getAllByText("Add to library"));
  expect(screen.getAllByText("Add to library")).toBeTruthy();
});

Question for @shadcn Would you consider merging those tests in if I made a separate PR after this gets merged in?

Jared-Dahlke avatar Oct 04 '23 15:10 Jared-Dahlke

@Jared-Dahlke — Wanted to chime in to make sure you're aware of Storybook's interaction tests?

That test you shared could be done entirely within a play function, which is part of the story. It also runs in a real browser instead of JSDom.

Then you could debug each step interactively.

Automating is done via test-runner, which will smoke test all stories and run any play functions that may exist.

The end result is similar (except for the browser vs. JSDom environment and better debugging), but with far less boilerplate:

import { Meta, StoryObj } from "@storybook/react";
+ import { within, userEvent } from '@storybook/testing-library';
+ import { expect } from '@storybook/jest';
import { Plus } from "lucide-react";
import { Button } from "@/registry/default/ui/button";
import {
  Tooltip,
  TooltipContent,
  TooltipTrigger,
} from "@/registry/default/ui/tooltip";
const meta: Meta<typeof Tooltip> = {
  title: "ui/Tooltip",
  component: Tooltip,
  tags: ["autodocs"],
  argTypes: {},
};
export default meta;
type Story = StoryObj<typeof Tooltip>;
export const Base: Story = {
  render: (args) => (
    <Tooltip {...args}>
      <TooltipTrigger asChild>
        <Button variant="outline" className="w-10 rounded-full p-0">
          <Plus className="h-4 w-4" />
          <span className="sr-only">Add</span>
        </Button>
      </TooltipTrigger>
      <TooltipContent>
        <p>Add to library</p>
      </TooltipContent>
    </Tooltip>
  ),
  args: {},
+  play: async ({ canvasElement }) => {
+    const canvas = within(canvasElement);
+    const buttonElement = canvas.getByRole("button");
+    await userEvent.hover(buttonElement);
+    await waitFor(() => canvas.getAllByText("Add to library"));
+    await expect(canvas.getAllByText("Add to library")).toBeTruthy();
+  },
};

kylegach avatar Oct 04 '23 19:10 kylegach

Just want to throw my thoughts out for including testing with the stories:

Personally I do use storybook's interaction testing on a lot of my components, but I think it adds an extra barrier to entry when imagining these stories as being part of shadcn/ui CLI. I love that shadcn/ui takes away a lot of the boilplate needed to work with radix primatives and doesn't bloat the files with things I might never need, while still allowing me to expand on the functionality if need.

In this way I would like to try keep the stories as simple as possible with no extra configuration to the .storybook/main.ts or .storybook/preview.tsx files. Anything past the storybook essentials should be given to the user to implement.

lloydrichards avatar Oct 15 '23 10:10 lloydrichards

@lloydrichards If we want to add a new style (Default and New York from CLI selection), would the storybook styling and examples need to be updated as well?

since each styles are with different icons and sizes.

linkb15 avatar Oct 31 '23 19:10 linkb15

would the storybook styling and examples need to be updated as well?

Technically there shouldn't be any styling in the stories since they pull the components in from the respective file. With either default or New York or their own overrides, the same story will work. It could be a good thing to review or keep in mind though, not to create too many custom elements in these stories to keep them generic of the theming

lloydrichards avatar Oct 31 '23 21:10 lloydrichards

Have made the stories for the new 0.5.0 components based off the documentations. Think there might be some interesting additional stories that could be added to show the examples, but left out for the time being. Anyone want to review or add suggestions, i'm all ears 👂 .

lloydrichards avatar Jan 04 '24 16:01 lloydrichards

Looks like it's going to take a long time before we see it deployed :/

pmm26 avatar Jan 05 '24 12:01 pmm26

@lloydrichards @pmm26 I've started testing it. It's a pretty big PR and I have to figure out to maintain it in the long term...etc. Definitely want to land this though... :+1:

shadcn avatar Jan 05 '24 12:01 shadcn

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Comments Updated (UTC)
ui ❌ Failed (Inspect) Jan 5, 2024 0:11am

vercel[bot] avatar Jan 05 '24 12:01 vercel[bot]

@shadcn I'm happy to help own the maintenance of the Storybook stuff ❤️

ShaunEvening avatar Jan 05 '24 13:01 ShaunEvening

It's a pretty big PR and I have to figure out to maintain it in the long term...etc.

Let me know if you need anything from me to help with this process. I was thinking about building a page in the docs that can quickly overview the storybook feature, but I understand that will only increase the amount to review so can draft it up as a new PR if that makes things easier. Excited to see this land, so willing to help and commit to any maintenance in the future like @Integrayshaun. 🙏 thanks!

lloydrichards avatar Jan 07 '24 18:01 lloydrichards

@lloydrichards I'll take a stab at reviewing the work this afternoon 👍

ShaunEvening avatar Jan 08 '24 13:01 ShaunEvening

Did some refactoring to add some documentation in the form of JSDocs as well reduced repetition through Story arg composition. Am still thinking about expanding the argType to be more verbose of the properties, especially for components where the ArgTable generation isn't automatic or for others that are not very descriptive. Screenshot 2024-01-22 at 23 18 14

@lloydrichards I'll take a stab at reviewing the work this afternoon 👍

If you see any other places where the story composition could be improved, i'm open for improving them 🙏


Something I noticed while adding some more explicit storied from the RadixUI props was that there doesnt seem to be default styling for disabled components like Accordion, RadioGroup, and Checkbox like there is Select or Switch. Maybe someone feels like making a new PR for this fix? Screenshot 2024-01-22 at 23 20 36

lloydrichards avatar Jan 22 '24 22:01 lloydrichards

Hey, folks. Love this effort! @ShaunEvening has asked me to take over as the "Storybook contributor voice" and I'm happy to do so. Will review this in the next few days!

kylegach avatar Jan 25 '24 20:01 kylegach

If you agree with my suggestions, I'm happy to put them in place, if that's helpful.

Thanks so much! I've already gone through and made a lot of these changes. There are a few components that rely on external props which still need the type on the variable, but i've used satisfies as well on these just to keep the patter going.

One thing I've notices is that with moving the render and args into the meta there are a lot of stories that are just:

export const Default: Story = {};

Is there a cleaner way to express this? Without loosing the JSDocs? Nothing too important, just something I noticed.

Finally, I could not actually run the project locally (details below), so I was unable to confirm the functionality of the Storybook. But everything looks good to me, afaict.

And for this, there was an issue with when i did the merge conflict, but this should be solved now. you'll need to be in the app/www directory to run pnpm storybook

lloydrichards avatar Jan 30 '24 19:01 lloydrichards