Documentation library for the foreman project

Usage no npm install needed!

<script type="module">
  import theforemanStories from 'https://cdn.skypack.dev/@theforeman/stories';



Documentation library for the foreman project

Package Version Downloads Status Build Status: Linux PRs Welcome code style: prettier

What are stories?

Stories are a playground that allows you to develop, document, and demo your components in isolation. It uses storybook, and some tailor-made configurations for foreman plugins.

Writing a story

You can write stories in two ways:

  1. Component Story Format (CSF) - https://github.com/storybookjs/storybook/blob/next/addons/docs/docs/mdx.md
  2. MDX - https://github.com/storybookjs/storybook/blob/next/addons/docs/docs/mdx.md

Assuming we have a Toggle component that lives in webpack/components/Toggle/Toggle.js:

import React from 'react';

const Toggle = ({ opened, setOpened }) => (
  <button onClick={() => setOpened(!opened)}>
    {opened ? 'OPEN' : 'CLOSE'}

export default Toggle;

To create a story for a given component, Toggle, first, create a story with the file name Toggle.stories.(js|mdx) next to Toggle.js. The tfm-stories will search for files with the .stories.(js|mdx) extension.

└── webpack
    └── components
        └── Toggle
            ├── Toggle.js
            └── Toggle.stories.js

Component Story Format (CSF)

Storybook’s Component Story Format (CSF) is the recommended way to write stories since Storybook 5.2. Read the announcement to learn more about how it came to be.

In CSF, stories and component metadata are defined as ES Modules. Every component story file consists of a required default export and one or more named exports.

import React from 'react';
import { action, boolean } from '@theforeman/stories';
import Toggle from './Toggle';

export default {
  title: 'Components|Widgets/Toggle',
  component: Toggle,

export const withState = () => {
  const [opened, setOpened] = useState(false);

  return <Toggle opened={opened} setOpened={setOpened} />;

export const withKnobs = () => (
  <Toggle setOpened={action('setOpened')} opened={boolean('opened')} />

export const opened = () => (
  <Toggle opened={true} />

export const closed = () => (
  <Toggle opened={false} />

Using this format will automatically generate a doc-page with the following features:

  1. The first story will be configured as the primary story.

  2. A Description slot is computed from the Component's docgen comments in the component's source. For example, here's the source for Badge:

     * Use `Badge` to highlight key info with a predefined status.
    export const Badge = ({ status, children }) => { ... }
  3. A prop-types table is computed from the component's docgen prop-types. For example, here's the source for Badge prop-types:

    Badge.propTypes = {
      /** Set the status of the badge */
      status: PropTypes.oneOf(['success', 'error', 'info']),
      /** Set the content of the badge */
      children: PropTypes.node,

Storybook Docs MDX

MDX is a syntax for writing long-form documentation and stories side-by-side in the same file. In contrast to DocsPage, which provides smart documentation out of the box, MDX gives you full control over your component documentation.

Read more about writing mdx stories: https://github.com/storybookjs/storybook/blob/master/addons/docs/docs/mdx.md

import { Meta } from '@theforeman/stories';
import { Story, Canvas, ArgsTable, action } from '@theforeman/stories';
// note: In Storybook v6 Preview was renamed to Canvas, and Props was renamed to ArgsTable

import Toggle from './Toggle';


# Toggle

Use `Toggle` to highlight key info with a predefined status.

<ArgsTable of={Toggle} />

  <Story name="With Knobs">
    <Toggle setOpened={action('setOpened')} opened={boolean('opened')} />

With `opened={true}`

  <Story name="opened">
    <Toggle opened={true} />

With `opened={false}`

  <Story name="closed">
    <Toggle opened={false} />

Run stories development server

tfm-stories [options]
  -V, --version                output the version number
  --plugin                     Use for a foreman-plugin
  -p, --port <number>          Port to run Stories (default: 6006)
  -s, --setup-file <filename>  Stories global setup file.
  -h, --help                   output usage information

Build stories

tfm-build-stories [options]
  -V, --version                output the version number
  --plugin, --plugin           Use for a foreman-plugin
  -s, --setup-file <filename>  Stories global setup file.
  -o, --output-dir <dir-name>  Directory where to store built files
  -w, --watch                  Enable watch mode
  -q, --quiet                  Suppress verbose build output
  -h, --help                   output usage information

Sorting stories

Stories will be sorted based on:

  1. The storyWeight number parameter where smaller values appear first. By default, all the stories have a storyWeight set to 1000.
  2. Alphabetically A-Z

To change the storyWeight for a given story, set it in the story parameter.


export default {
  title: 'Components/SomeComponent',
  component: SomeComponent,
  parameters: {
    storyWeight: 100,


import { Meta } from '@theforeman/stories';

    storyWeight: 100,

Available storybook addons


Storybook Docs transforms your Storybook stories into world-class component documentation.

DocsPage. Out of the box, all your stories get a DocsPage. DocsPage is a zero-config aggregation of your component stories, text descriptions, docgen comments, props tables, and code examples into clean, readable pages.

MDX. If you want more control, MDX allows you to write long-form markdown documentation and stories in one file. You can also use it to write pure documentation pages and embed them inside your Storybook alongside your stories.

Import components from @storybook/addon-docs directly:

import { Story, Canvas, ArgsTable } from '@storybook/addon-docs';

See: https://github.com/storybookjs/storybook/tree/next/addons/docs


@storybook/addon-actions can be used to display data received by event handlers in Storybook.

To use it in your stories, import the action method from @storybook/addon-actions and assign it to an event handler.

import { action } from '@storybook/addon-actions';

export const myByttonStory = () => <Button onClick={action('buttonClicked')}>Click Here</Button>;


@storybook/addon-knobs allow you to edit props dynamically using the Storybook UI. You can also use Knobs as a dynamic variable inside stories in Storybook.

To use it in your stories, import the knobs that you need from @storybook/addon-knobs and assign them to props. See available knobs: https://github.com/storybookjs/storybook/tree/master/addons/knobs#available-knobs

import { boolean, text, number } from '@storybook/addon-knobs';

// Knobs for React props
export const withAButton = () => (
  <button disabled={boolean("Disabled", false)}>
    {text("Label", "Hello Storybook")}

// Knobs as dynamic variables.
export const asDynamicVariables = () => {
  const name = text("Name", "Arunoda Susiripala");
  const age = number("Age", 89);

  const content = `I am ${name} and I'm ${age} years old.`;
  return <div>{content}</div>;


@storybook/addon-centered was an addon that, in previous Storybook versions, could be used to center components inside the preview in Storybook. As of Storybook v6 it is no longer needed.

To center a story:

  export const MyStory = () => <div>my story</div>;
  MyStory.story = {
    parameters: { layout: 'centered' },

layout also accepts padded and fullscreen.


@storybook/addon-storysource is used to show stories source in the addon panel.


@storybook/addon-viewport allows your stories to be displayed in different sizes and layouts in Storybook. This helps build responsive components inside of Storybook.


Please checkout the contributing.md, the roadmap.md and the open issues.