Axon 911 Frontend DocsAxon 911 Frontend Docs
Axon 911 Frontend Docs
    Loading navigation…
U
User
Switch to light mode
  1. Working On Features
  2. Storybook Stories

Storybook Stories

When creating components for reuse, you can document them in our Storybook for designers and developers alike. The purpose of the stories should be to show the different visual states of the component, and document the component's props (args in Storybook) and their possible values for developer's. In ui-core package, alongside the component files you will find the majority of *.stories.tsx files. There are also in other ui packages as well as the dispatch app. If you are unfamiliar with writing stories, please review this doc in Storybook for an introduction. Here we will review a few guidelines to keep stories standardized, no matter who writes them. Coming soon: Link to Cursor prompt to help write stories
Document props with args and argTypes
Defining argTypes in Storybook will specify the controls used for each arg (prop) that a story will take. Using the Meta config to define argTypes allows us to set all the controls a story will have available. In the individual story you can decide to list them all, include or exclude args to make the story simpler with a smaller subset of interactive controls. Here's an example:
The above will render out controls for id, defaultValue, disabled and indicatorPosition args. The id control will be shown as required with an asterisk. The disabled arg will have a switch control for booleans.
indicatorPosition since its a prop that takes IndicatorPositionEnum values, uses enumToArgType util
that will return an argType object that renders a select control with appropriate values and labels that correspond to the enum.
All args will be sorted in alphabetical order. From here, subsequent stories will inherit the same controls list.
The Default story
Start with a general or "Default" story of a basic example. This default story should list all the props as args that the component accepts, so the engineer can get an idea of the component's API. If you define all the argTypes in the Meta object, then you should get this list in the Default story.
Represent the states and variants necessary
Try to have a story for each variant or state that the component can be in. In these stories, args edits should impact the story - e.g. when the viewer uses a control to set or change a value, the story should change. In order to do this, the component rendered in the story should accept the args as props (args passed to the render function).
Consider limiting args shown in a story with include or exclude, in order to only expose a few that make sense to edit (like the "Disabled" story example below).
Stories for different states
In stories that represent a particular state, include only the args whose controls will change will have an impact on that state. To achieve this, we can include or exclude the controls and make stories with particular states simpler. For example, if we want a "Disabled" story to show the disabled state, we can only include a couple of args that are necessary to allow the viewer to manipulate those args.
include and exclude are described in the Storybook docs on argTypes. Gotcha: If you use include or exclude, be sure to pass args (or _args if not using them) to the render function or it will be ignored.
Stories that show multiple variants
Sometimes it makes sense to group a bunch of variations in one story and show them side by side, like when they are colors, icons or positions for example. If you have a story that shows all, the controls will probably not be used to change props for all the rendered components. You can still list all the args to show the API, but use the util disableArgControls to disable their controls, since changing values shouldn't have any effect.
WithSnapshot to activate Chromatic snapshots
We have disabled snapshots globally for all stories, so you need to be intentional about which stories you want to snapshot. Try and snapshot at least one story representation per component, espercially stories that illustarte multiple variants (colors, positions, disabled). Wrap your story in the WithSnapshot helper in order to Chromatic snapshots for that story. Local publishes use pnpm chromatic from the repo root.
Stories for components that own a GraphQL fragment
A component that reads its data with useFragment has no data of its own: a route's query supplies it (see GraphQL Fragments). A story for it needs that parent query's result in an Apollo cache, with data masking on, so the component sees only its own fragment's fields, as it does in the app. withApollo from @prepared911/util-storybook-helpers/apollo does this. It builds a real client with createApolloClient, seeds the parent operation into its cache with cache.writeQuery, and wraps the story in ApolloProvider. There is no network and no mock server: the client's only link fails any request, so a story that asks for something it was not seeded with errors instead of hanging. SeededQuery reads the seeded operation (useQuery with cache-only) and hands you data, which is the same shape as the probe in the testing recipe (Mocking GraphQL for Tests).
  • data is typed as the operation's unmasked result, fragment fields included, so a mistyped or missing field fails tsc. The component still sees only its fragment: ask for a field it did not select and you get undefined, as in production.
  • If the operation has required variables, variables is required, and you pass the same values to withApollo and to SeededQuery.
  • Each mount gets its own client, with the Apollo devtools off, and the client is stopped on unmount, so stories do not leak caches.
  • Apply the decorator in meta.decorators or on one story, never in .storybook/preview.ts. A global decorator makes every story depend on it, so Chromatic's TurboSnap (onlyChanged) would rebuild all of them on any change to it.
  • Use leaf fragments. A component that renders children with their own fragments needs nested fragment spreads to resolve through masking, which depends on CORE-1303 until that lands.
  • withApollo works in any Storybook host. To import ./queries.graphql in a story, the host's viteFinal adds graphqlOperationsPlugin from @prepared911/tooling-config/vite/graphql (it resolves the import to the generated .graphql.ts, so run pnpm codegen first), and the host's package.json lists @prepared911/util-storybook-helpers as a dependency (packages/ui has none today).
  • Mocking requests with MSW is a follow-up and not part of this decorator.
  • Component tests use renderWithApollo from @prepared911/util-testing, which answers through MSW handlers rather than a seeded cache. Both run with data masking on, so a component sees only its own fragment's fields either way (see GraphQL Fragments).
Functional tests
We use Storybook with the vitest addon in order to trun stories into tests. At the basic level even if it doesn't have explicit tests, each component will test if it renders and error if it throws an error while rendering. Consider testing interactive components by adding a play method to the story.
You can create a particular story just for tests if it makes sense.
You can use the "Interaction Recorder" tool in Storybook to help generate a test.
When testing, folow the same guidelines as our e2e tests and test things as they are seen by the user (not classnames, test ids) whenever possible.
Previous
Working on features / UI Components
Next
Working with data / State Management
On this page
Document props with args and argTypes
The Default story
Represent the states and variants necessary
Stories for different states
Stories that show multiple variants
WithSnapshot to activate Chromatic snapshots
Stories for components that own a GraphQL fragment
Functional tests