Documentation
Custom Elements Manifest provides a wealth of information about each component. After generating a manifest, it will provide documentation for each component in Storybook.
Providing Documentation with Custom Elements Manifest and Storybook#
We've got a stable library package that could be published to npm so others can reuse the components. It should be simple, right? An engineer installs the @in/ui
package and imports the component where applicable, but then is faced with a dilemma. How does any of this work? Savvy engineers may inspect the type definitions to get a sense of how a component works, but those lack any context for the HTML attributes which are a heavily used interface for custom elements. The engineer would then have to inspect the source, which is compiled to ES2015 and wrapped in a strange decorator pattern. To solve this dilemma, we can provide documentation for the end-users of the component library.
So far, you've used Storybook as a development environment. For each use case, you coded a Story and provided a template and arguments that generated Controls for the Storybook Canvas. Storybook also has built-in tooling to display documentation for each component in the Docs tab. The first Story for each component is prioritized in the Docs tab. The user has the option to display the template in code beneath an example of the Story. When available, any HTML attributes or methods the component exposes are displayed in a table beneath the example.

By default, only the example is displayed for each Story in the Docs tab in Storybook. We need to provide additional documentation for each HTML attribute and method for Storybook to display contextual information about HTML attributes, methods, the tag name, or possibly description of the component. Storybook autogenerates the documentation with a tool called the Custom Elements Manifest. The custom-elements-manifest
package analyzes each component class
definition, parses HTML attributes, events, methods, the tag name, and description of the component and generates a JSON file which Storybook can use to configure the documentation for each Story. Custom Elements Manifest uses a CLI to run the script that generates the custom-elements.json file.
xxxxxxxxxx
custom-elements-manifest analyze --dev --exclude "[./**/*.stories.js, dist]"
You'll eventually run the above command in the packages/component
directory that produces a custom-elements.json
file Storybook will interpret for parts of the documentation. To provide additional context around each HTML attribute or method, Storybook also interprets JSDoc comments.
JSDoc#
JSDoc is a standardized comment pattern for documenting JavaScript inline. Any comment written in JSDoc syntax above a particular code block documents the immediately proceeding code. For instance, a JSDoc comment above a class
definition provides contextual information about the class
, while a JSDoc comment above a method documents the method.
xxxxxxxxxx
/**
* Renders a dialog.
* @tag {string} in-dialog
* @param {string} style - css presentation
*/
@Component({
A document for getting started with JSDoc is available. In short, the multi-line JSDoc comment begins with a description of the code block, followed by contextual tags. function
arguments can be tagged with @param
. HTML attributes can be denoted with @attr
. After documenting each component with JSDoc, we'll have a format Storybook can interpret while generating the documentation for each component in the UI library. As an added bonus, TypeScript should compile type definitions with the inline JSDoc comments, so engineers can read the documentation right in their IDE.
Before generating the custom-elements.json
file, document one component in the @in/ui
package with JSDoc.
Starting with the ButtonComponent class
in packages/component/src/button/Button.ts
, document the class
definition with JSDoc. After providing a description, use the @tag
tag to define the tag name of the component. Use @attr
tags to document the necessary HTML attributes that effect the display of ButtonComponent
.
xxxxxxxxxx
/**
* Renders a button.
* @tag {string} in-button
* @attr {string} is - "in-button", Required
* @attr {string} class - Variant, can either be "primary" "secondary", or "icon"
*/
@Component({
For Storybook to uptake these changes, you'll have to generate the custom-elements.json
file which contains a JSON schema which Storybook uses to interpret each component for the documentation. We have a problem though, mostly caused by running Storybook in the context of a lerna monorepo. Storybook expects the custom-elements.json
file to be in the root directory. If we ran the Custom Elements Manifest Analyzer in the packages/component
directory, it would generate paths to each component implementation relative to that directory and not the root directory. The analyzer expects JavaScript, not TypeScript, so we have to build the @in/ui
package first so the tool can parse the output in the dist/fesm2015
directory. When that happens the custom-elements.json
file will be written to packages/component
with paths like in the following example:
xxxxxxxxxx
{
"kind": "javascript-module",
"path": "dist/fesm2015/index.js",
"exports": [
{
"kind": "js",
"declaration": {
"name": "ButtonComponent",
"module": "./src/button"
}
}
]
Scripting the Build#
You'll need to build the @in/ui
package, run the analyze script, further process the custom-elements.json
by replacing the paths so they are relative to the root directory, then write the custom-elements.json
file to the root directory. That will allow Storybook to properly interpret the manifest and display documentation for each component.
The work begins in the packages/component/package.json
file where the script is called. The analyze
script already cleans the dist directory, builds the library and runs the analyzer.
xxxxxxxxxx
"analyze": "yarn clean && yarn build && custom-elements-manifest analyze --dev --exclude \"[./**/*.stories.js]\"",
Run the script using lerna.
xxxxxxxxxx
lerna exec --scope @in/ui -- yarn analyze
Observe the custom-element.json
file is outputted in the packages/component directory
. If you open the file you'll see paths relative to this directory. Storybook requires the same file to be placed in the root directory of the repository, so we have to update the paths programmatically everytime this script runs. To do that with node, make a new file in the packages/component
directory named manifest.js
.
xxxxxxxxxx
touch packages/component/manifest.js
Open the manifest.js
and import the function
needed to read and write files and process paths.
xxxxxxxxxx
import { readFile, writeFile } from "fs";
import { resolve } from "path";
Call readFile
, passing in the three arguments required by the function
: the path to the file, the format ("utf-8"), and the function callback where you'll process the contents of the file.
xxxxxxxxxx
readFile(resolve("./custom-elements.json"), "utf8", (err, content) => {});
This lesson preview is part of the Fullstack Web Components course and can be unlocked immediately with a \newline Pro subscription or a single-time purchase. Already have access to this course? Log in here.
Get unlimited access to Fullstack Web Components, plus 70+ \newline books, guides and courses with the \newline Pro subscription.
