UI Component Library

What is a Component Library

This component library is a (growing) collection of all the React components and hooks that are available within the getsentry/sentry codebase.

The library consists of the framework itself, which helps to make stories available & discoverable, as well as the story content. Each story is meant to describe how to use a React Component or hook; what are the properties, behaviors, and common or recommended combinations.

Accessing and Using the Library

The Component Library is implemented as a webpage inside the main sentry application. Whenever Sentry is running, the Library is available.

  • Prod Access
    Log in and then visit /stories/ on your existing Sentry Org. This works the same on sentry.io or for self-hosted installations.

  • Dev Access
    Whether you are using yarn dev-ui to start a development server, or sentry devserver to spin up all the services locally, you can access the Component Library by logging in and changing the path from /issues/ (the default landing page) to /stories/.

Creating new Stories

Using the storyBook() framework

The easiest way to start documenting a single component is to import and use storyBook().

Here's a template to copy, edit, and get started with:

Copied
// Filename: components/button.stories.tsx

import {Button} from 'sentry/components/button'; // replace with your component
import storyBook from 'sentry/stories/storyBook';

export default storyBook(Button, story => {
  story('Default', () => <Button>Default Button</Button>);
});

This template includes all the basics:

  1. The filename is button.stories.tsx and sits directly beside button.tsx which is the component being described.
  2. The file uses a default export, there is no need to name things when there is only one export.
  3. To create the storyBook() we're passing in the Button component directly as the first argument. This sets the title at the top of the page, and avoids typos.
  4. We've implemented a function that accepts story as it's argument. This is inspired by jest and the describe() function.
  5. Each time we call story() we can render out a unit onto the page. This includes a title, and the rendered component example. This is similar to jest and the test() or it() function, with the exception that story is not a global.

Using this framework you get some nice things automatically:

  • Stories are printed in the same order they are written in the source file
  • Each story() callback can be async

Some tips to remember:

  • Don't be afraid to import anything else you need to help illustrate use cases for your component. It's useful to leverage <p> and <Fragment> in most cases for example.
  • If you are demonstrating a component that relies on a global hook like useOrganization() or usePageFilters(), then you should wrap your story with those context providers manually.
  • Named exports, or a mix of named and default exports are all supported. This is more useful with non-framework stories.
  • If you are testing a hook then you must pass a string as the first argument to storyBook() instead of the hook itself because.

Non-framework stories

It is possible to skip the storyBook() framework function and build stories from scratch.

Doing this allows full flexibility to define your story.

Using default exports, named exports, or a mixture of each is fully supported. But take note that the order they will be rendered is not consistent/defined.

Look at icons.stories.tsx for an example.

Helper Components

There are some helper components specifically built to make common patterns in stories easier to implement. Of course you can import anything in the sentry repo to make your stories richer, and create new helpers to make expressing stories easier!

Helper Components:

  • <JSXNode />
    Render a formatted JSX component name and some properties.

    Copied
    return <JSXNode name="IconFire" props={{color: 'red400', size: 'sm'}} />;
    
    // will render as <code>&lt;IconFire<br/>color="red"<br/>size="sm" \:gt;</code>
  • <JSXProperty /> Render a formatted JSX property name & value.

    Copied
    return <JSXProperty name="disabled" value={false} />;
    
    // will render as `<code>disabled={false}</code>`
  • <SideBySide>{children}</SideBySide>
    A shortcut for display: flex; to render multiple items in a row, wrapping as needed

    Copied
    return (
      <SideBySide>
        <Badge type="default">Default</Badge>
        <Badge type="alpha">Alpha</Badge>
        <Badge type="beta">Beta</Badge>
      </SideBySide>
    );
  • <SizingWindow />
    A wrapper component to help demonstrate what the component looks like when the parent size is different or changing.
    By default uses display:flex; overflow: hidden; which can test responsive components well. Can also be set to display: block; overflow: auto;.

    Copied
    <SizingWindow style={{height: '100px'}}>
      <LoadingTriangle />
    </SizingWindow>`
  • <Matrix />
    A helper to render multiple pairs of properties and values in a grid. For example, we can render N values for one props, against M values for another prop. The example below compares 4 values for button size against 5 values for priority, resulting in a 20 example buttons in a grid.

    Copied
    <Matrix
      render={() => Button}
      propMatrix={{
        priority: ['default', 'primary', 'danger', 'link', undefined],
        size: ['md', 'sm', 'xs', 'zero'],
      }}
      selectedProps={['priority', 'size']}
    />

Ownership & Next Steps

Since all the code for the stories framework is home-grown, anyone can tinker to create features and integrations to make the experience of discovering, reading, and writing, organizing stories much better.

Obviously, documenting a component is a valuable addition to the Component Library. Create a new file and give it a try!

Some ideas for you to tinker with:

  • General: Design facelift.
  • File Tree: Fix cutoff labels, Maybe by side-scrolling? Click+drag to make file tree wider?
  • File Tree: Sort folder to the top.
  • File Tree: Add component search. Bonus points if we can search by more than just the filename.
  • Stories: Automatically generate a list of component props from typescript types.
  • Stories: Include a Code-Viewer, so people can see the code that powers a story inline.
  • Stories: Update TS types so storyBook() function can accept a hook as the first argument, in addition to JSXElementConstructor.
  • Helpers: Create a system to reference related components. "Button is related to Link & Link Button". Bonus points if we can verify those references at build time so they don't go stale.
  • Helpers: Create a helper (iframe?) to simulate browser window sizes, so @media CSS queries can be exercised.
  • GetSentry: Create a hook that getsentry can implement so stories from there are included.
  • Tests: Ability to use stories as test fixtures, so we can visually see fixtures that tests use. ie: tests that click buttons and verify render is updated.
You can edit this page on GitHub.