-
Notifications
You must be signed in to change notification settings - Fork 10
Open
Description
like discussed on discord :)
the idea is to be able to use custom jsdoc annotations to document things in storybook
based on this work https://github.com/shoelace-style/shoelace/blob/dafb35c6e210193a9ca31efddc3429ba2bb66be3/custom-elements-manifest.config.js
for example, we can use @since annotation to get a version this could be in turn used to be displayed in storybook.
i did some hacking around with storyblocks and i got something based on this plugin
annotation example based on shoelace example
/**
* A button is used to trigger an action. For more information about a button, click [here](https://unify.nedap.healthcare/42a5b6c3c/p/17c689-button/b/05c6fc)
*
* @status stable
* @since 2.0.0
*
* @dependency uc-icon
* @dependency uc-badge
*
**/the cem plugin
import {parse} from 'comment-parser';
function noDash(string) {
return string.replace(/^\s?-/, '').trim();
}
export function customTags() {
return {
name: 'custom-tags',
analyzePhase({ts, node, moduleDoc}) {
switch (node.kind) {
case ts.SyntaxKind.ClassDeclaration: {
const className = node.name.getText();
const classDoc = moduleDoc?.declarations?.find(declaration => declaration.name === className);
const customTags = ['aria-rules', 'deprecated-attribute','dependency', 'status', 'since'];
let customComments = '/**';
node.jsDoc?.forEach(jsDoc => {
jsDoc?.tags?.forEach(tag => {
const tagName = tag.tagName.getText();
if (customTags.includes(tagName)) {
customComments += `\n * @${tagName} ${tag.comment}`;
}
});
});
// This is what allows us to map JSDOC comments to ReactWrappers.
classDoc['jsDoc'] = node.jsDoc?.map(jsDoc => jsDoc.getFullText()).join('\n');
const parsed = parse(`${customComments}\n */`);
parsed[0].tags?.forEach(t => {
switch (t.tag) {
// custom rule for aria
case 'aria-rules':
if (!Array.isArray(classDoc['aria-rules'])) {
classDoc['aria-rules'] = [];
}
classDoc['aria-rules'].push({
name: t.name,
description: noDash(t.description),
});
break;
// custom deprecated attribute rule
case 'deprecated-attribute':
if (!Array.isArray(classDoc['deprecated-attribute'])) {
classDoc['deprecated-attribute'] = [];
}
classDoc['deprecated-attribute'].push({
name: t.name,
description: noDash(t.description),
});
break;
// custom deprecated element rule
case 'deprecated':
if (!Array.isArray(classDoc['deprecated'])) {
classDoc['deprecated'] = [];
}
classDoc['deprecated'].push({
description: noDash(t.description),
});
break;
// Dependencies
case 'dependency':
if (!Array.isArray(classDoc['dependencies'])) {
classDoc['dependencies'] = [];
}
classDoc['dependencies'].push(t.name);
break;
// Value-only metadata tags
case 'since':
case 'status':
classDoc[t.tag] = t.name;
break;
// All other tags
default:
if (!Array.isArray(classDoc[t.tag])) {
classDoc[t.tag] = [];
}
classDoc[t.tag].push({
name: t.name,
description: t.description,
type: t.type || undefined
});
}
});
}
}
}
}
}preview.ts
import { setCustomElementsManifest } from "@storybook/web-components";
import { setWcStorybookHelpersConfig } from "wc-storybook-helpers";
import customElements from "custom-elements.json";
import DocumentationTemplate from "./DocumentationTemplate.mdx";
setWcStorybookHelpersConfig({ typeRef: "expandedType", hideArgRef: true });
setCustomElementsManifest(customElements);
const preview: Preview = {
// ... more settings and things
docs: {
page: DocumentationTemplate,
},
}
export default preview;DocumentationTemplate.mdx
import { Meta, Title, Subtitle, Primary, Controls, Stories, Description } from '@storybook/blocks';
import MyCustomBlock from './MyCustomBlock';
{/*
* π The isTemplate property is required to tell Storybook that this is a template
* See https://storybook.js.org/docs/api/doc-block-meta
* to learn how to use
*/}
<Meta isTemplate />
<Title />
<Subtitle/>
<Description />
<MyCustomBlock />
# Default implementation
<Primary />
## Inputs
The component accepts the following inputs (props):
<Controls />
---
## Additional variations
Listed below are additional variations of the component.
<Stories />MyCustomBlock.jsx
import React, { useContext } from "react";
import { DocsContext } from "@storybook/addon-docs/blocks";
import customElements from "@nedap/unify-components/custom-elements.json";
import { getComponentByTagName } from "wc-storybook-helpers/dist/cem-utilities.js";
const MyCustomBlock = ({ children, ...props }) => {
const context = useContext(DocsContext);
const tag = context.storyById().component;
const component = getComponentByTagName(tag, customElements);
const { since, dependencies, status } = component;
return (
<div {...props}>
{since && <p>Since: {since}</p>}
{dependencies && (
<p>
Dependencies:
<ul>
{dependencies.map((dep) => (
<li>{dep}</li>
))}
</ul>
</p>
)}
{status && <p>Status: {status}</p>}
{/* Render your custom content or children */}
{children}
</div>
);
};
export default MyCustomBlock;Metadata
Metadata
Assignees
Labels
No labels