Simulando dados para o Storybook
O Storybook é uma ótima ferramenta para desenvolvimento e testes isolados, podendo acelerar bastante o tempo de desenvolvimento.
O <MockResolver /> permite carregar com facilidade fixtures ou interceptors para ver como diferentes respostas de rede podem se parecer. Ele pode ser aninhado, composto e até usado para fetches imperativos, geralmente usados com endpoints que têm efeitos colaterais, como getList.push e update.
Configuração
- Resource
- Component
export class Article extends Entity {
id: number | undefined = undefined;
content = '';
author: number | null = null;
contributors: number[] = [];
static key = 'Article';
}
export const ArticleResource = resource({
urlPrefix: 'http://test.com',
path: '/article/:id',
schema: Article,
searchParams: {} as { maxResults: number },
});
export let ArticleFixtures: Record<string, Fixture[]> = {};
import { useSuspense } from '@data-client/react';
import { ArticleResource } from 'resources/ArticleResource';
import ArticleSummary from './ArticleSummary';
export default function ArticleList({
maxResults,
}: {
maxResults: number;
}) {
const articles = useSuspense(ArticleResource.getList, { maxResults });
return (
<div>
{articles.map(article => (
<ArticleSummary key={article.pk()} article={article} />
))}
</div>
);
}
Fixtures
Vamos testar três casos com nossos fixtures e interceptors: alguns resultados interessantes na lista, uma lista vazia e dados inexistentes, de modo que o fallback de carregamento seja exibido.
// leave out in production so we don't bloat the bundle
if (process.env.NODE_ENV !== 'production') {
ArticleFixtures = {
full: [
{
endpoint: ArticleResource.getList,
args: [{ maxResults: 10 }] as const,
response: [
{
id: 5,
content: 'have a merry christmas',
author: 2,
contributors: [],
},
{
id: 532,
content: 'never again',
author: 23,
contributors: [5],
},
],
},
{
endpoint: ArticleResource.update,
response: ({ id }, body) => ({
...body,
id,
}),
},
],
empty: [
{
endpoint: ArticleResource.getList,
args: [{ maxResults: 10 }] as const,
response: [],
},
],
error: [
{
endpoint: ArticleResource.getList,
args: [{ maxResults: 10 }] as const,
response: {
message: 'Bad request',
status: 400,
name: 'Not Found',
},
error: true,
},
],
loading: [],
};
}
Decorators
Você precisará adicionar os decorators globais apropriados para estabelecer o contexto correto.
Isso deve se parecer com o que você adicionou na configuração inicial
import { Suspense } from 'react';
import { DataProvider, AsyncBoundary } from '@data-client/react';
export const decorators = [
Story => (
<DataProvider>
<AsyncBoundary>
<Story />
</AsyncBoundary>
</DataProvider>
),
];
Story
Envolver nosso componente com o <MockResolver /> nos permite controlar de forma declarativa como os fetches do Reactive Data Client são resolvidos.
Aqui selecionamos quais fixtures devem ser usados pelos controles do storybook.
import { type StoryObj } from '@storybook/react';
import { MockResolver } from '@data-client/test';
import type { Fixture } from '@data-client/test';
import ArticleList from 'ArticleList';
import { ArticleFixtures } from 'resources/ArticleResource';
export default {
title: 'Pages/ArticleList',
component: ArticleList,
argTypes: {
result: {
description: 'Results',
defaultValue: 'full',
control: {
type: 'select',
options: Object.keys(ArticleFixtures),
},
},
},
};
export const FullArticleList: StoryObj<{ result: keyof typeof ArticleFixtures }> =
{
render: ({ result }) => (
<MockResolver fixtures={ArticleFixtures[result]}>
<ArticleList maxResults={10} />
</MockResolver>
),
args: { result: 'full' },
};