QUI React

React Documentation

A component library is practically useless without good documentation.

Our documentation serves two purposes:

  • It provides the developer with a way to work with the components as they're developed.
  • It provides the library consumers with usage instructions, live examples, and guidelines.

The qui/react-docs package is a React application based on QUI Docs, our React documentation framework.

Page Creation

Documentation pages are located in src/routes. Refer to the Page Setup documentation to learn more.

Component Demos

Using Tag component as an example, let's review the file structure:

routes
components.tag._index
demos(demo components)
index.ts(demo component exports)
showcase.tsx(example component)
route.mdx(component documentation mdx)

Goals

  • Each component's demo serves as a self-contained example of the component. Our documentation utilities ensure that each demo component's source code is automatically synced with the rendered component.
  • tailwindcss is used for layout. In most cases, we align the example in the center of the Demo panel.
  • Each component's input properties are documented using custom prop utilities.

Let's add our first demo component.

Example Component

Add the following file to the tag/demos directory:

// tag/demos/example-tag.tsx
import {ReactNode} from "react"
import {QTag} from "@qui/react"
export default function ExampleTag(): ReactNode {
return (
<div className="grid justify-center">
<QTag dismissable label="Action" />
</div>
)
}

We'll also need to update the demo directory's index.ts file to ensure that our demo is usable.

Code Demo Overview

Our demos are defined in a specific format required by our custom CodeDemo component, which handles syntax highlighting and component rendering.

We refer to each standalone example as a node, which consists of a component (the renderable example) and one or more source files (the source code of the example).

import {ElementType} from "react"
interface CodeDemoNode {
node: {
component: ElementType
source: {code: string; fileName: string}[]
}
}

Demo Exports

// tag/demos/index.ts
import {CodeDemoNode} from "@qui/mdx-docs"
import Showcase from "./showcase"
import ShowcaseSource from "./showcase.tsx?raw"
export default {
ExampleTag: {
component: Showcase,
source: [{code: ShowcaseSource, fileName: "showcase.tsx"}],
} satisfies CodeDemoNode,
}

With the demo complete, we'll learn about documenting component properties.

Prop Documentation

Property documentation is automatically generated from the JSDoc tags on our interfaces.

Recall the QTag component's interface from the previous section:

/**
* @public
* @interface
*/
export type QTagProps<C extends As = DefaultAs> =
PolymorphicComponentPropsWithRef<
C,
{
/**
* The component used for the root node. It can be a React component or
* element.
*
* @default 'button'
*/
as?: C
/**
* Used to manually control checked state. Does nothing if {@link toggleable}
* is `false`
*/
checked?: boolean
/**
* Default input value.
*/
defaultValue?: boolean
/**
* If true, the component is disabled.
*
* @default false
*/
disabled?: boolean
/**
* Render a close icon that dismisses the tag when clicked.
*
* @default false
*/
dismissable?: boolean
/**
* Callback fired when user clicks on the tag when {@link toggleable} is
* `true`.
*/
onChange?: (event: SyntheticEvent, value: boolean) => void
/**
* If true, the component is toggleable between on/off states.
*
* @default false
*/
toggleable?: boolean
// ... omitted for brevity
}
>

1. Interface Tags

Our parser looks for TypeScript interfaces with the @public tag. Since these interfaces are ultimately hoisted to a global context provider, we whitelist only our component props interfaces for performance reasons (it's a lot of data). The JSON is ultimately bundled with the application.

The @interface tag tells our parser to expand extended types, if any. This rolls the extended types up into a single consolidated interface.

/**
* @public
* @interface
*/
export type QTagProps<C extends As = DefaultAs> =
PolymorphicComponentPropsWithRef<C>
// ...

2. JSDoc Authoring

Add a description for every property

Our parser automatically collects and processes JSDoc comments on our interfaces. Every property should have a description that aptly describes the property, e.g.:

type Example = {
/**
* Default input value.
*/
defaultValue?: boolean
}

WARNING

Properties that are not tagged with a JSDoc comment are ignored. As a general rule, document every property.

Add @default tags, where applicable.

If a property has a default value, ensure that you've added a @default tag with the corresponding value.

For strings, use single quotes, e.g. 'button'

For booleans, use the raw boolean value, e.g. false

interface Example {
/**
* The component used for the root node. It can be a React component or element.
*
* @default 'button'
*/
as?: C
/**
* If true, the component is toggleable between on/off states.
*
* @default false
*/
toggleable?: boolean
}

If your description refers to another property on the interface, you should tag it with an @link tag. The rendered documentation table will examine these links and generate anchor links to those properties.

type Example = {
/**
* Callback fired when user clicks on the tag when {@link toggleable} is
* `true`.
*/
onChange?: (event: SyntheticEvent, value: boolean) => void
/**
* If true, the component is toggleable between on/off states.
*
* @default false
*/
toggleable?: boolean
}

The parser also picks up links to external resources:

interface Example {
/**
* React {@link https://react.dev/learn/passing-props-to-a-component#passing-jsx-as-children children} prop.
*/
children?: ReactNode
}

For an overview of every available tag, visit the official JSDoc documentation.

3. Referencing Props

In the MDX file, you will need to import the TypeDocProps component from the shared docs package.

If you properly tagged your props interface with @public, it will be available by name in our documentation site's global context. The TypeDocProps component searches this context for the matching name:

<TypeDocProps name="QTagProps" />

Continue to the final step for an example of this.

Markdown Documentation

In this final step, we'll author the component docs in the index.mdx file.

---
title: Tag
---
import {CodeDemo, TypeDocProps} from "@qui/mdx-docs"
import Demos from "./demos"
# {frontmatter.title}
#### Tags allow users to categorize content. They can represent keywords or people and are grouped to describe an item or search request.
## Overview
Tags display a single category, dimension, or property related to adjacent content.
## Examples
```tsx
import {QTag} from "@qui/react"
```
### Showcase
<CodeDemo expanded node={Demos.ExampleTag} />
## API
### Props
<TypeDocProps name="QTagProps" />

Recap

  • Every component must contain usage guidelines and working examples.
  • Strive to align your documentation with the corresponding Angular component, where applicable.
  • All demo components are located in the demos folder of the corresponding page. The demos folder also contains an index.ts barrel file. Follow the Demo Exports example to ensure that your demos are consumable.
  • Ensure that every interface property has a JSDoc comment that adheres to the JSDoc Authoring Guidelines.

Writing documentation is tedious, but necessary. Hopefully these tools make the process a little easier.