storybook-addon
Storybook addon for tramvai apps
Installation
You need to install @tramvai/storybook-addon
:
- npm
- Yarn
npm install @tramvai/storybook-addon
yarn add @tramvai/storybook-addon
And connect addon in the storybook configuration file:
.storybook/main.js
module.exports = {
addons: ["@tramvai/storybook-addon"]
}
Features
- Providers for DI container
- Providers for router
- Providers for
react-query
- Page actions support
- tramvai
babel
configuration - tramvai
postcss
configuration
How to
Access to DI container
- page.tsx
- page.stories.tsx
import { LOGGER_TOKEN } from '@tramvai/tokens-common';
export const Page = () => {
const logger = useDi(LOGGER_TOKEN);
logger.info('render');
return (
<h1>Page</h1>
);
}
import type { TramvaiStoriesParameters } from '@tramvai/storybook-addon';
import { Page } from './page';
// You can pass to DI container any reducers, providers and modules
const parameters: TramvaiStoriesParameters = {
tramvai: {
stores: [],
initialState: {},
providers: [],
modules: [],
},
};
export default {
title: 'Page',
component: Page,
parameters,
};
export const PageStory = () => <Page />;
Router hooks and components
- page.tsx
- page.stories.tsx
import { Link, useUrl } from '@tramvai/module-router';
export const Page = () => {
const url = useUrl();
return (
<>
<h1>Page at {url.pathname}</h1>
<p>
<Link url="/third/">to third page</Link>
</p>
</>
);
}
import type { TramvaiStoriesParameters } from '@tramvai/storybook-addon';
import { Page } from './page';
// You can pass to router current route and url
const parameters: TramvaiStoriesParameters = {
tramvai: {
currentRoute: { name: 'second', path: '/second/' },
},
};
export default {
title: 'Page',
component: Page,
parameters,
};
export const PageStory = () => <Page />;
React Query
- page.tsx
- page.stories.tsx
import { createQuery, useQuery } from '@tramvai/react-query';
const query = createQuery({
key: 'base',
fn: async () => {
return { foo: 'bar' };
},
});
export const Page = () => {
const { data, isLoading } = useQuery(query);
return (
<>
<h1>Page</h1>
<p>
{isLoading ? 'Loading...' : data.foo}
</p>
</>
);
}
import type { TramvaiStoriesParameters } from '@tramvai/storybook-addon';
import { Page } from './page';
// You can pass to router QueryClient default options
const parameters: TramvaiStoriesParameters = {
tramvai: {
reactQueryDefaultOptions: {
queries: {
refetchOnMount: false,
refetchOnReconnect: false,
refetchOnWindowFocus: false,
},
},
},
};
export default {
title: 'Page',
component: Page,
parameters,
};
export const PageStory = () => <Page />;
Page actions running
- page.tsx
- page.stories.tsx
import { declareAction } from '@tramvai/core';
const serverAction = declareAction({
name: 'server-action',
fn() {
console.log('server action');
},
conditions: {
onlyServer: true,
},
});
const browserAction = declareAction({
name: 'browser-action',
fn() {
console.log('browser action');
},
conditions: {
onlyBrowser: true,
},
});
export const Page = () => {
return (
<h1>Page</h1>
);
}
Page.actions = [serverAction, browserAction];
import type { TramvaiStoriesParameters } from '@tramvai/storybook-addon';
import { Page } from './page';
// Actions with `onlyBrowser` condition will be executed
const parameters: TramvaiStoriesParameters = {
tramvai: {
actions: Page.actions,
},
};
export default {
title: 'Page',
component: Page,
parameters,
};
export const PageStory = () => <Page />;
Http clients with real requests
- page.tsx
- page.stories.tsx
import { declareAction } from '@tramvai/core';
const httpRequestAction = declareAction({
name: 'http-request-action',
async fn() {
return this.deps.httpClient.get('/');
},
deps: {
httpClient: HTTP_CLIENT,
},
});
export const Page = () => {
return (
<h1>Page</h1>
);
}
Page.actions = [httpRequestAction];
import { HttpClientModule } from '@tramvai/module-http-client';
import type { TramvaiStoriesParameters } from '@tramvai/storybook-addon';
import { Page } from './page';
// HttpClientModule is required for real requests
const parameters: TramvaiStoriesParameters = {
tramvai: {
actions: Page.actions,
modules: [HttpClientModule],
},
};
export default {
title: 'Page',
component: Page,
parameters,
};
export const PageStory = () => <Page />;
How to provide environment variables?
This addon provides a few important defaults:
- Mock provider for
ENV_MANAGER_TOKEN
- Read
env.development.js
content from application root
So, any variables from env.development.js
will be registered in envManager
.
If you want to add custom variables for some stories, pass options
for CommonTestModule
(from @tramvai/test-mocks
package) in story parameters:
const parameters: TramvaiStoriesParameters = {
tramvai: {
options: {
env: {
FOO: 'BAR',
},
},
},
};
Troubleshooting
"Rendered more hooks than during the previous render."
In case of using both fastRefresh
and strictMode
in reactOptions
in Storybook config in main.js, you might see the error message above.
This is a known issue in Storybook itself, as a temporary workaround you can simply disable the strictMode
in Storybook config.
Won't work:
.storybook/main.js
module.exports = {
reactOptions: {
fastRefresh: true,
strictMode: true,
}
};
Works:
.storybook/main.js
module.exports = {
reactOptions: {
fastRefresh: true,
}
};
Contribute
For testing changes in this plugin locally, you need a few steps:
- [tramvai repo] Copy
examples-standalone/storybook
application to different folder, e.g.storybook-app
- [storybook-app] run
git init
in this folder (@storybook/builder-webpack5
uses as root the first parent directory containing.git
folder) - [storybook-app] Update there all
tramvai
dependencies inpackage.json
- [tramvai repo] Copy plugin build output from
packages/tramvai/storybook-addon/lib
- [storybook-app] Paste into the
storybook-app/node_modules/@tramvai/storybook-addon/lib
folder - [storybook-app] Run
storybook
in nested foldercd storybook && npm run storybook