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

docs: generate documentation for UI components #5849

Merged
merged 17 commits into from
Dec 13, 2023
Merged

Conversation

shahednasser
Copy link
Member

@shahednasser shahednasser commented Dec 12, 2023

What

This PR introduces the necessary tooling to generate JSON spec files for React components with props and component details. Its created to be used with the Medusa UI package.

react-docs-generator

A new custom tool that combines react-docgen and typedoc to generate spec JSON files for React components.

Although react-docgen provides the type of specs we need, it presents complications and caveats, where some props aren't documented or their types aren't inferred properly (more on that in the next section). So, where necessary, Typedoc fills in the gaps of missing props, descriptions, or types. As React Docgen is updated to resolve some of these issues, we can maybe in the future rely on it only.

Usage

After installing and building dependencies in the docs-util package, run the following command in the docs-util/packages/react-docs-generator directory:

yarn start --src ~/medusa/packages/design-system/ui/src --output ~/medusa/www/apps/ui/src/specs

Pointers on Writing and Inferring UI doc blocks

This section covers some things to keep in mind both when reviewing this PR and performing changes on the UI package moving forward (also open to suggestions for alternative ways of handling certain points if they don't make sense from a development standpoint):

  • All React components must have their display names explicitly defined, especially if they're extending a third-party component. If a React component's display name can't be inferred it won't be included in the outputted documentation. For example:
/**
 * Groups multiple items together.
 */
const Group = SelectPrimitive.Group
Group.displayName = "Select.Group"
  • React docgen doesn't infer prop types for components created with forwardRef. The props type has to be specified again for the destructured props.

For example, we previously wrote the Avatar component like this:

interface AvatarProps
  extends Omit<
      React.ComponentPropsWithoutRef<typeof Primitives.Root>,
      "asChild" | "children" | "size"
    >,
    VariantProps<typeof avatarVariants> {
  src?: string
  fallback: string
}

const Avatar = React.forwardRef<
  React.ElementRef<typeof Primitives.Root>,
  AvatarProps
>(
  (
    { src, fallback, variant = "rounded", size = "base", className, ...props },
    ref
  ) => {
// ....

Now, we change it to clearly specify AvatarProps as the type of the destructured props:

interface AvatarProps
  extends Omit<
      React.ComponentPropsWithoutRef<typeof Primitives.Root>,
      "asChild" | "children" | "size"
    >,
    VariantProps<typeof avatarVariants> {
  src?: string
  fallback: string
}

/**
 * This component is based on the [Radix UI Avatar](https://www.radix-ui.com/primitives/docs/components/avatar) primitive.
 */
const Avatar = React.forwardRef<
  React.ElementRef<typeof Primitives.Root>,
  AvatarProps
>(
  (
    { 
      src, 
      fallback, 
      variant = "rounded", 
      size = "base",
      className, 
      ...props
    }: AvatarProps,
    ref
  )
  • Comments for props have to be directly added to props (not using the @prop tag as we do with TSDoc) like so:
{ 
      /**
       * The URL of the image used in the Avatar.
       */
      src, 
      /**
       * Text to show in the avatar if the URL provided in `src` can't be opened.
       */
      fallback, 
      /**
       * The style of the avatar.
       */
      variant = "rounded", 
      /**
       * The size of the avatar's border radius.
       */
      size = "base",
      className, 
      ...props
    }: AvatarProps,

They can also be added to the props within the interface definition, however, unless necessary we should keep them at the component's level. @prop is only used in cases where it's not possible to directly add a comment for a prop. In that case, it's picked up by Typedoc.

  • Types of props are only inferred correctly from an interface rather than a type. So, where possible, I've changed types into interfaces in the UI package. In scenarios where the change isn't possible, Typedoc fills in the gap. In those cases, we should add the @interface tag to the type's comments like so:
/**
 * @interface
 */
type CalendarProps =
  | ({
      mode: "single"
    } & SingleProps)
  | ({
      mode?: undefined
    } & SingleProps)
  | ({
      mode: "range"
    } & RangeProps)
  • By default, react-docgen only generates documentation for props either directly defined in the interface or destructured in the component's definition function. In react-docs-generator we create the necessary customization to only output documentation for props having a docblock. This ensures that no unnecessary props that are either defined in the underlying third-party components or HTML attributes are outputted in the documentation.
  • For cases where we use a prop defined in the underlying third-party component that we don't want to document, or where the prop has a dockblock in the underlying third-party component, we add the custom @excludeExternal tag to the docblock of the component. This ensures that no props defined in any underlying component are part of the generated documentation. For example:
/**
 * This component is based on the [react-date-picker](https://www.npmjs.com/package/react-date-picker) package.
 * 
 * @excludeExternal
 */
const Calendar = ({
  • To ensure a prop is included in the output documentation regardless of other conditions (such as where it's defined), we use the custom @keep tag. For example:
/**
   * Whether to show days of previous and next months.
   * 
   * @keep
   */
  showOutsideDays = true,
  • To ensure a prop is ignored in the output documentation regardless of other conditions, we use the custom @ignore tag. For example:
/**
   * @ignore
   */
  classNames,

Changes in the UI docs

  • Generated new spec files in the www/apps/ui/src/specs directory.
  • Removed the ComponentProps component and replaced it with ComponentReference which renders components' descriptions and props based on the generated specs.
  • Modified the Rehype plugin to inject specs into ComponentReference
  • Removed existing props registry
  • Small changes to the props table design.

Other

  • A GitHub action that re-generates the spec files when release is published.
  • General improvements and changes to the docs-util workspace based on necessary changes for the new react-docs-generator tool.

Copy link

changeset-bot bot commented Dec 12, 2023

⚠️ No Changeset found

Latest commit: 4e8450f

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

Copy link

vercel bot commented Dec 12, 2023

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

Name Status Preview Comments Updated (UTC)
api-reference ✅ Ready (Inspect) Visit Preview 💬 Add feedback Dec 13, 2023 1:55pm
docs-ui ✅ Ready (Inspect) Visit Preview 💬 Add feedback Dec 13, 2023 1:55pm
medusa-docs ✅ Ready (Inspect) Visit Preview 💬 Add feedback Dec 13, 2023 1:55pm

Copy link
Contributor

@kasperkristensen kasperkristensen left a comment

Choose a reason for hiding this comment

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

LGTM!

One question: If I update the inline documentation will it then get reflected in the docs as soon as the PR hits develop?

@shahednasser
Copy link
Member Author

No it'll only be reflected if the command is ran to generate the spec files. I've added a GitHub action that runs it either when a new release is out or if we manually trigger the action

…cy-input.tsx

Co-authored-by: Kasper Fabricius Kristensen <45367945+kasperkristensen@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants