A Storybook add-on to write stories for Relay components.
github_demo.webm
Install the @imchhh/storybook-addon-relay
package using the package manager of your choice:
yarn add -D @imchhh/storybook-addon-relay
Add @imchhh/storybook-addon-relay
to the addons
list in your .storybook/main.c?(j|t)s
file:
const config: StorybookConfig = {
addons: [
// ...
'@imchhh/storybook-addon-relay',
],
};
Add a relay
field to your story's parameters
.
export const Default = {
parameters: {
relay: {
query: graphql`...`,
getReferenceEntry: (data) => ['data', data.node],
variables: {},
mockResolvers: {},
},
},
};
query
: AGraphQLTaggedNode
returned by the Relay'sgraphql
template literal. You should pass the query operation that uses the@relay_test_operation
directive.getReferenceEntry
: A function that returns an entry to be added to the story's args. It takes the result of theuseLazyLoadQuery
hook with the query passed as a parameter and returns an entry to be added to the story's args.variables
: Optional. Variables to pass to the query.mockResolvers
: Optional. A mock resolver object passed to therelay-test-utils
'MockPayloadGenerator.generate
function.generateFunction
: Optional. A function to execute instead of the defaultMockPayloadGenerator.generate
function.
Here is a minimal example:
// UserAvatar.tsx
export const UserAvatar = (props) => {
const data = useFragment(
graphql`
fragment UserAvatar on User {
profileImageUrl
}
`,
props.user,
);
return <img src={data.profileImageUrl} alt="" />;
};
// UserAvatar.stories.tsx
import { StoryObj } from '@storybook/react';
import { graphql } from 'react-relay';
import { UserAvatar } from './UserAvatar';
export default {
component: UserAvatar,
};
export const Default: StoryObj = {
parameters: {
relay: {
query: graphql`
query UserAvatarStoryQuery @relay_test_operation {
node(id: "test-id") {
... on User {
...UserAvatar
}
}
}
`,
getReferenceEntry: queryResult => ['user', queryResult.node],
mockResolvers: {
User: () => ({
profileImageUrl: 'https://source.unsplash.com/random/400x400',
}),
},
},
},
};
If you are using TypeScript 4.9 or later, you can use the WithRelayParameters
interface and the satisfies
keyword to get type-safe:
// UserAvatar.stories.tsx
import { WithRelayParameters } from '@imchhh/storybook-addon-relay';
import { StoryObj } from '@storybook/react';
import { graphql } from 'react-relay';
import { UserAvatarStoryQuery } from '~/path/of/relay/artifacts';
import { UserAvatar } from './UserAvatar';
export default {
component: UserAvatar,
};
export const Default: StoryObj = {
parameters: {
relay: {
query: graphql`
query UserAvatarStoryQuery @relay_test_operation {
node(id: "test-id") {
... on User {
...UserAvatar
}
}
}
`,
// Now `queryResult` is typed!
getReferenceEntry: queryResult => ['user', queryResult.node],
mockResolvers: {
User: () => ({
profileImageUrl: 'https://source.unsplash.com/random/400x400',
}),
},
} satisfies WithRelayParameters<UserAvatarStoryQuery>,
},
};
And you can pass the Resolvers
type, which generated via GraphQL Code Generator, as the second type parameter to WithRelayParameters
.
// UserAvatar.stories.tsx
import { WithRelayParameters } from '@imchhh/storybook-addon-relay';
import { StoryObj } from '@storybook/react';
import { graphql } from 'react-relay';
import { Resolvers } from '~/path/of/codegen/generated';
import { UserAvatarStoryQuery } from '~/path/of/relay/artifacts';
import { UserAvatar } from './UserAvatar';
export default {
component: UserAvatar,
};
export const Default: StoryObj = {
parameters: {
relay: {
query: graphql`
query UserAvatarStoryQuery @relay_test_operation {
node(id: "test-id") {
... on User {
...UserAvatar
}
}
}
`,
getReferenceEntry: queryResult => ['user', queryResult.node],
// Now `mockResolvers` is typed!
mockResolvers: {
User: () => ({
profileImageUrl: 'https://source.unsplash.com/random/400x400',
}),
},
} satisfies WithRelayParameters<UserAvatarStoryQuery, Resolvers>,
},
};
These are totally optional, so feel free to skip them
I don't have any plans to write or set up a contribution guide. If this library doesn't solve your problem or isn't sufficient, please create an issue and describe your situation or suggestion. I would appreciate it very much.
MIT