Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve typing on generated storybook components #6226

Merged
merged 17 commits into from
Aug 26, 2022

Conversation

will-ks
Copy link
Contributor

@will-ks will-ks commented Aug 17, 2022

This PR fixes a couple of issues with the storybook stories generated for each component.

  1. Currently if you're using TypeScript with strict mode, the story contains a type error due to args not being properly typed:

Screenshot 2022-08-17 at 14 55 39

This PR adds type assertion to the generated story, so the args parameter is now properly typed.

  1. The story, viewed in Storybook, shows a warning about the component not being set up for controls:

Screenshot 2022-08-17 at 14 49 30

This PR sets the component field in the story metadata. This means that the generated story will now show auto-generated controls for the component's props, and removes the warning:

Screenshot 2022-08-17 at 14 49 23

@nx-cloud
Copy link

nx-cloud bot commented Aug 17, 2022

☁️ Nx Cloud Report

CI is running/has finished running commands for commit a138f50. As they complete they will appear below. Click to see the status, the terminal output, and the build insights.

📂 See all runs for this branch


✅ Successfully ran 4 targets

Sent with 💌 from NxCloud.

@netlify
Copy link

netlify bot commented Aug 17, 2022

Deploy Preview for redwoodjs-docs ready!

Name Link
🔨 Latest commit a138f50
🔍 Latest deploy log https://app.netlify.com/sites/redwoodjs-docs/deploys/63076c5f4eca7600085ce647
😎 Deploy Preview https://deploy-preview-6226--redwoodjs-docs.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify site settings.

@will-ks will-ks changed the title Feat/story types Improve typing on generated storybook components Aug 17, 2022
@dac09 dac09 added the release:fix This PR is a fix label Aug 18, 2022
@dac09 dac09 assigned jtoar and dac09 and unassigned noire-munich Aug 18, 2022
@dac09
Copy link
Collaborator

dac09 commented Aug 18, 2022

Thank you so much for this @will-ks - was on our list but couldn't get to yet!

@will-ks
Copy link
Contributor Author

will-ks commented Aug 18, 2022

I just realised there's also stories generated for pages, layouts etc, which should also be given the same treatment. I'll close this PR and reopen when I've fixed the other ones.

@will-ks will-ks closed this Aug 18, 2022
@will-ks will-ks reopened this Aug 18, 2022
@will-ks
Copy link
Contributor Author

will-ks commented Aug 18, 2022

Components, Pages, Layouts and Cells now have the changes.
Only exception is with Cells, I couldn't set the component prop, as it expects a single component but the stories are each rendering a different component. I'm not sure what the best way around that would be.
Also note that I had to change the null that the cell stories were returning to an empty fragment (<></>) as Storybook expects a JSX.Element.

@jtoar
Copy link
Contributor

jtoar commented Aug 22, 2022

Thanks @will-ks! I'm not great with TS, but one question: should we use ComponentStory instead of Story from @storybook/react? Two reasons for the suggestion:

// Post component

const Post = ({
  title,
  description,
}: {
  title: string
  description: string
}) => {
  return (
    <div>
      <h2>{title}</h2>
      <p>{description}</p>
    </div>
  )
}

export default Post

With Story:

image

With ComponentStory:

image

@jtoar
Copy link
Contributor

jtoar commented Aug 22, 2022

TODO: add ComponentMeta

@will-ks
Copy link
Contributor Author

will-ks commented Aug 23, 2022

@jtoar Good find, does look like ComponentStory is more appropriate. Will change. Also nice to read from that source you provided that in Storybook v7 stories get more concise. Hopefully upgrading to v7 is on the roadmap for Redwood.

@dac09 dac09 mentioned this pull request Aug 23, 2022
18 tasks
@dac09
Copy link
Collaborator

dac09 commented Aug 23, 2022

Thanks again @will-ks -

Just added a commit to regenerate stories in our fixtures (which we use for smoke-tests). The changes are looking good to me, except for in Cells, as you've mentioned previously - essentially we can't spread the args because the type is unknown. I wonder if we can somehow use our utility types for cells to address this.

The other thing we discussed was also whether its worth including the type ComponentMeta - this is in the storybook docs (https://storybook.js.org/docs/react/writing-stories/args) - when you switch to the TS tab. I'm not exactly sure what it's purpose is, apart from maybe if the story itself is imported? Maybe in Redwood it doesn't matter since we automatically import it for you anyway?

Screenshot 2022-08-23 at 16 48 47

@jtoar
Copy link
Contributor

jtoar commented Aug 25, 2022

Hopefully upgrading to v7 is on the roadmap for Redwood.

@will-ks it certainly is, but it's not actually out yet right? Just alpha releases so far. So in the near future!

@jtoar
Copy link
Contributor

jtoar commented Aug 25, 2022

@dac09 I tried using the loading utility type (CellLoadingProps) in the Cell file and it did fix the red squiggles for the Loading story in the stories file. But there's no empty utility type, so what do you think the move would be there?

Here's an alternative: let's just not pass args to Loading and Empty because they don't take any props in the template:

export const Loading = () => <div>Loading...</div>
export const Empty = () => <div>Empty</div>

So this...

export const loading: ComponentStory<typeof Loading> = (args) => {
  return Loading ? <Loading {...args} /> : <></>
}

export const empty: ComponentStory<typeof Empty> = (args) => {
  return Empty ? <Empty {...args} /> : <></>
}

would become...

export const loading = () => {
  return Loading ? <Loading /> : <></>
}

export const empty = () => {
  return Empty ? <Empty /> : <></>
}

Thoughts?

@jtoar
Copy link
Contributor

jtoar commented Aug 25, 2022

We actually run into the "Spread types may only be created from object types.ts(2698)" error with pages too. Anything that doesn't have any props specified.

E.g., here's the AboutPage in the test project fixture:

import { Link, routes } from '@redwoodjs/router'
import { MetaTags } from '@redwoodjs/web'

const AboutPage = () => { // 👈 no props
  return (
    <p className="font-light">
      This site was created to demonstrate my mastery of Redwood: Look on my
      works, ye mighty, and despair!
    </p>
  )
}

export default AboutPage

You'll see a red squiggle in the story file:

import type { ComponentMeta, ComponentStory } from '@storybook/react'

import AboutPage from './AboutPage'

export const generated: ComponentStory<typeof AboutPage> = (args) => {
  return <AboutPage {...args} /> // 👈 red squiggle here
}

export default {
  title: 'Pages/AboutPage',
  component: AboutPage,
} as ComponentMeta<typeof AboutPage>

@Tobbe
Copy link
Member

Tobbe commented Aug 25, 2022

I'm not great with Storybook - we just recently started using it at work
So when I first saw this

export const loading: ComponentStory<typeof Loading> = (args) => {
  return Loading ? <Loading {...args} /> : <></>
}

I was super confused about the spreading of ...args on <Loading /> exactly because I knew it didn't take any props. I thought maybe Storybook needed/used the extra args for something but eventually figured out it didn't.

So removing the args is a great improvement imho.

export const loading = (args) => {
return Loading ? <Loading {...args} /> : null
export const loading: ComponentStory<typeof Loading> = (args) => {
return Loading ? <Loading {...args} /> : <></>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a stylistic preference, but I'd probably do this:

Suggested change
return Loading ? <Loading {...args} /> : <></>
return <Loading {...args} /> || <></>

Copy link
Contributor

@jtoar jtoar Aug 25, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking at this again, do we actually need to handle this case? (Where Loading isn't exported by the Cell right?) If Loading isn't defined, isn't TS going to complain when we import it further up?

import { Loading, Empty, Failure, Success } from './PostCell'

It looks like here we'll get something like Module '"./PostCell"' has no exported member 'Loading'.ts(2305).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That would be even better! Just make sure it works the same with strict mode on/off
And JS doesn't care, right? So for the JS template we'd still have to do something like this, yeah?

@jtoar
Copy link
Contributor

jtoar commented Aug 25, 2022

Now that I'm more into it, it's a little more nuanced than I thought:

  • The component template doesn't have props, so we'd end up in the same TS-unhappy args situation. But components are obviously going to have props. (Sure some don't but most do.) So if we remove args, it's now an exercise for the user to add args back in
  • Pages conditionally have props if they take route parameters

So it seems like args is something you want to add after you've added props. (Which for components, happens imminently after generation—not some far-off thing in the future really.) So now we're back to the question of should generators generate unused code that educates users about things they'll need later. Or not? We could always include this as a comment.

@virtuoushub I know we added args in to get the "Show code" feature working. Is there another nuance here we're missing maybe? (It hooks up the controls add-on too right?)

@Tobbe
Copy link
Member

Tobbe commented Aug 25, 2022

  • The component template doesn't have props, so we'd end up in the same TS-unhappy args situation. But components are obviously going to have props. (Sure some don't but most do.) So if we remove args, it's now an exercise for the user to add args back in

So maybe the thing to change here is to give generated components some dummy prop? That way we could keep the args spreading for components.

And for pages we could generate the stories with args spreading if the page is generated for a path with parameters (yarn rw g page post /post/{id})

@jtoar
Copy link
Contributor

jtoar commented Aug 25, 2022

@Tobbe do you think if we implemented this RFC #5833 (adding interface to TS generators), maybe this'd just be a solved problem?

Copy link
Member

Tobbe commented Aug 25, 2022

Not sure it'd be a solved problem, but doing that is definitely part of the solution here. I didn't plan to bring that one up again until we were on React18 though, because with React18 they improved the React.FC type. Personally though, I'm all for making the change right now 🙂

- cell -> loading / empty, don't pass args
- component -> explain args
- page -> only pass args to route params page
import ${pascalName}Page from './${pascalName}Page'

export const generated: ComponentStory<typeof ${pascalName}Page> = (args) => {
return <${pascalName}Page ${propValueParam} {...args} />
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Am I correct in thinking that propValueParam is used to provide a default value, but you can then let {...args} spread over it to render with a different value for that prop?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah that's right!

sb_args.mov

@Tobbe Tobbe enabled auto-merge (squash) August 26, 2022 09:26
@Tobbe Tobbe merged commit 6f11bc7 into redwoodjs:main Aug 26, 2022
@redwoodjs-bot redwoodjs-bot bot added this to the next-release milestone Aug 26, 2022
@virtuoushub
Copy link
Collaborator

@virtuoushub I know we added args in to get the "Show code" feature working. Is there another nuance here we're missing maybe? (It hooks up the controls add-on too right?)

fwiw - Yes, I think it has to do with the controls add-on. Outside of that I do not really know the ramifications for removing it out side of what you already listed.


I have not dealt with TypeScript/Redwood in a little bit - Do we generate the component's props as well? I think we could have the generated stories adhere to the correct typings in strict mode if we do unless I am missing something.

Copy link
Member

Tobbe commented Aug 27, 2022

Currently we generate all components without props. We know most components will probably have props, but it's just really difficult to generate sensible default props, because it varies so much what props you'll want/need

@virtuoushub
Copy link
Collaborator

Currently we generate all components without props. We know most components will probably have props, but it's just really difficult to generate sensible default props, because it varies so much what props you'll want/need

Makes sense, imho we might want to consider at least "boiler-plating" empty props since we are generating the components and we know most people will want props. This way we can punt on what sensible defaults are, but at least provide the scaffolding to allow a framework user to easily add in what is needed later.

Copy link
Member

Tobbe commented Aug 27, 2022

100% agree with that. And it's the route we're going to take when we upgrade to React 18, because then we can also start using the improved React.FC type

dac09 added a commit to dac09/redwood that referenced this pull request Aug 28, 2022
…-performance

* 'main' of github.com:redwoodjs/redwood: (25 commits)
  chore(deps): update dependency @types/jsonwebtoken to v8.5.9 (redwoodjs#6309)
  chore(deps): update dependency @nhost/nhost-js to v1.4.10 (redwoodjs#6306)
  fix(deps): update dependency fastify to v4.5.3 (redwoodjs#6313)
  fix(deps): update jest monorepo to v29.0.1 (redwoodjs#6314)
  docs: fix typos (redwoodjs#6310)
  fix(deps): update dependency dotenv-webpack to v8.0.1 (redwoodjs#6301)
  chore(deps): update dependency @auth0/auth0-spa-js to v1.22.3 (redwoodjs#6299)
  Adds a Deploy Keys section to server doc (redwoodjs#6304)
  Improve typing on generated storybook components (redwoodjs#6226)
  Update yarn.lock (in docs)
  fix(deps): update dependency @graphql-codegen/cli to v2.11.7 (redwoodjs#6300)
  try reverting tough-cookie patch change (redwoodjs#6298)
  Update yarn.lock
  v2.2.3
  fix(deps): update dependency @types/jest to v28.1.8 (redwoodjs#6294)
  remove testURL
  Update yarn.lock
  chore(deps): update dependency @playwright/test to v1.25.1 (redwoodjs#6283)
  fix(deps): update dependency @graphql-yoga/common to v2.12.8 (redwoodjs#6297)
  chore(deps): update yarn to v3.2.3 (redwoodjs#6284)
  ...
@jtoar jtoar modified the milestones: next-release, v3.0.0 Sep 2, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
release:fix This PR is a fix
Projects
Status: Done
Development

Successfully merging this pull request may close these issues.

None yet

6 participants