UI Component Library
/stories/
on your existing Sentry Org/Install.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, as well as 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 usingyarn dev-ui
to start a development server, orsentry 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:
// 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:
- The filename is
button.stories.tsx
and sits directly besidebutton.tsx
which is the component being described. - The file uses a default export; there is no need to name things when there is only one export.
- To create the
storyBook()
we're passing in theButton
component directly as the first argument. This sets the title at the top of the page, and avoids typos. - We've implemented a function that accepts
story
as its argument. This is inspired byjest
and thedescribe()
function. - 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 tojest
and thetest()
orit()
function, with the exception thatstory
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()
orusePageFilters()
, 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.
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.Copiedreturn <JSXNode name="IconFire" props={{color: 'red400', size: 'sm'}} />; // will render as <code><IconFire<br/>color="red"<br/>size="sm" \:gt;</code>
<JSXProperty />
Render a formatted JSX property name & value.Copiedreturn <JSXProperty name="disabled" value={false} />; // will render as `<code>size={false}</code>`
<SideBySide>{children}</SideBySide>
A shortcut fordisplay: flex;
to render multiple items in a row, wrapping as neededCopiedreturn ( <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 usesdisplay:flex; overflow: hidden;
which can test responsive components well. Can also be set todisplay: 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 all buttonpriority
values against all possiblesize
values which results in a 4x5 matrix of outputs.Copied<Matrix render={() => Button} propMatrix={{ priority: ['default', 'primary'], size: ['md', 'sm'], }} 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 toJSXElementConstructor
. - 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.