Skip to content
This repository has been archived by the owner on Apr 4, 2022. It is now read-only.

feat: add storybook #7

Merged
merged 1 commit into from
Mar 10, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 6 additions & 4 deletions app/templates/.gitignore
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
/.history
/.idea
node_modules
/dist
/build
/coverage
/src/assets/tailwind.parsed.css
dist/
build/
coverage/
tailwind.parsed.css
.env
.jest-test-results.json
storybook/
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import React from 'react';
import { RecoilRoot } from 'recoil';
import { addDecorator } from '@storybook/react';

addDecorator(
(Story) => (
<RecoilRoot>
<Story />
</RecoilRoot>
)
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import React from 'react';
import { Provider } from 'react-redux';
import store from '../../src/state/store';
import { addDecorator } from '@storybook/react';

addDecorator(
(Story) => (
<Provider store={store}>
<Story />
</Provider>
)
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import React from 'react';
import addons, { types } from '@storybook/addons';
import path from 'path';

const ADDON_ID = 'storybook/coverage';
const PANEL_ID = `${ADDON_ID}/panel`;
const PANEL_TITLE = 'Coverage';
const coverage = {};

function normalize(name) {
return name.toLowerCase().replace(/-/, '');
}

function loadCoverage() {
try {
const finds = require.context('../../../../../coverage/react', true, /index\.html$/);
finds.keys().forEach((file) => {
const dir = path.dirname(file);
const componentName = dir.split('/').pop();
coverage[normalize(componentName)] = file.replace('./', '');
});
} catch (e) {
console.error('[Storybook/Addon coverage] coverage not found', e);
}
}

function renderCoverage(api) {
const pathname = window.location.pathname;
return (args) => {
const storyData = api.getCurrentStoryData();
if (args.key !== PANEL_ID || !args.active || !storyData || !storyData.kind) {
return;
}
const componentName = storyData.kind.split('/').pop();
const test = componentName && coverage[normalize(componentName)];
if (!test) {
return;
}
return React.createElement('iframe', {
key: test,
src: pathname + test,
style: {
width: '100%',
height: '100%'
}
});
};
}

function registerCallback(api) {
addons.addPanel(PANEL_ID, {
type: types.PANEL,
title: PANEL_TITLE,
render: renderCoverage(api)
});
};

try {
loadCoverage();
addons.register(ADDON_ID, registerCallback);
} catch (e) {
console.error('[Storybook/Addon coverage]', e);
}
6 changes: 6 additions & 0 deletions app/templates/packages/project/.storybook/config/console.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { setConsoleOptions } from '@storybook/addon-console';

const panelExclude = setConsoleOptions({}).panelExclude;
setConsoleOptions({
panelExclude: [...panelExclude, /deprecated/],
});
11 changes: 11 additions & 0 deletions app/templates/packages/project/.storybook/config/i18n.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import React from 'react';
import { I18nProvider } from '../../src/i18n';
import { addDecorator } from '@storybook/react';

addDecorator(
(Story, { globals }) => (
<I18nProvider locale={globals.locale}>
<Story />
</I18nProvider>
)
);
9 changes: 9 additions & 0 deletions app/templates/packages/project/.storybook/config/layout.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { create } from '@storybook/theming';

export default create({
base: 'light',
brandTitle: '<%= projectNameCanonical %> react components',
brandUrl: 'https://mrmilu.com',
brandImage:
'https://pbs.twimg.com/profile_images/1226078968878641152/cLaoB--o_400x400.png'
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { addDecorator } from '@storybook/react';
import { withPerformance } from 'storybook-addon-performance';

addDecorator(withPerformance);
6 changes: 6 additions & 0 deletions app/templates/packages/project/.storybook/config/tests.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { addDecorator } from '@storybook/react';
import { withTests } from '@storybook/addon-jest';

import results from '../../src/stories/.jest-test-results.json';

addDecorator(withTests({ results }));
72 changes: 72 additions & 0 deletions app/templates/packages/project/.storybook/main.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
const { default: addons, mockChannel } = require('@storybook/addons');
const path = require('path');
const tsconfig = path.resolve(__dirname, '../tsconfig.json');

addons.setChannel(mockChannel());

module.exports = {
stories: [
'../@(src|documentation)/**/*.@(story|stories).@(tsx|mdx)'
],
addons: [
'@storybook/preset-create-react-app',
'@storybook/addon-actions',
{
name: '@storybook/addon-docs',
options: {
configureJSX: true,
babelOptions: {},
sourceLoaderOptions: null
}
},
{
name: '@storybook/addon-storysource',
options: {
rule: {
test: [/\.stories\.tsx$/],
include: [path.resolve(__dirname, '../src')]
},
loaderOptions: {
prettierConfig: { printWidth: 80, singleQuote: true }
}
// injectStoryParameters: true
}
},
'@storybook/addon-essentials',
'storybook-addon-material-ui',
'@storybook/addon-a11y',
'@storybook/addon-jest',
'./addon/addon-coverage/register',
'storybook-addon-performance/register',
'@storybook/addon-links',
{
name: 'storybook-addon-turbo-build',
options: {
// Please refer below tables for available options
optimizationLevel: 2
}
}
],
typescript: {
check: true,
checkOptions: { tsconfig },
reactDocgen: 'react-docgen-typescript',
reactDocgenTypescriptOptions: {
shouldExtractLiteralValuesFromEnum: true,
shouldExtractValuesFromUnion: false,
skipChildrenPropWithoutDoc: false,
shouldRemoveUndefinedFromOptional: true,
tsconfigPath: tsconfig
// propFilter: prop => !/^(testID)$/.test(prop.name),
}
},
webpackFinal: async (config, args) => {
config.module.rules.push({
test: /\.html$/,
use: {
loader: 'html-loader'
}
});
return config;
}
};
16 changes: 16 additions & 0 deletions app/templates/packages/project/.storybook/manager.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { addons } from '@storybook/addons';
import layoutTheme from './config/layout';

addons.setConfig({
isFullscreen: false,
showNav: true,
showPanel: true,
panelPosition: 'bottom',
sidebarAnimations: true,
enableShortcuts: false,
isToolshown: true,
selectedPanel: undefined,
initialActive: 'sidebar',
showRoots: false,
theme: layoutTheme
});
46 changes: 46 additions & 0 deletions app/templates/packages/project/.storybook/preview.js.ejs
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import './config/console';
import './config/performance';
import './config/tests';
import './config/i18n';
<%_ if (hasRecoil) { -%>
import './config/recoil';
<%_ } -%>
<%_ if (hasRedux) { -%>
import './config/redux';
<%_ } -%>

export const parameters = {
actions: { argTypesRegex: '^on[A-Z].*' },
controls: { expanded: true },
layout: 'centered',
options: {
storySort: {
order: [
'Introduction',
['Docs']
]
}
},
previewTabs: {
canvas: { title: 'Code', hidden: false },
'storybook/docs/panel': { title: 'Documentation' }
}
};

export const globalTypes = {
locale: {
name: 'Locale',
description: 'Internationalization locale',
defaultValue: 'en',
toolbar: {
icon: 'globe',
items: [
{ value: 'en', right: '🇺🇸', title: 'English' },
{ value: 'es', right: '🇪🇸', title: 'Español' },
{ value: 'fr', right: '🇫🇷', title: 'Français' },
{ value: 'zh', right: '🇨🇳', title: '中文' },
{ value: 'kr', right: '🇰🇷', title: '한국어' }
]
}
}
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { Meta } from '@storybook/addon-docs/blocks';

<Meta title="Introduction/Docs" parameters={{
docsOnly: true,
previewTabs: {
canvas: { hidden: true },
}
}} />

# Documentation

This is a documentation for the project and components
5 changes: 4 additions & 1 deletion app/templates/packages/project/jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@ const cfg = config('react', __dirname);

cfg.setupFilesAfterEnv.unshift(path.resolve(__dirname, './react.setup-after-env.js'));
cfg.transform = {
'^.+\\.tsx?$': 'babel-jest'
'^.+\\.tsx?$': 'babel-jest',
'^.+\\.mdx$': '@storybook/addon-docs/jest-transform-mdx'
};
cfg.collectCoverageFrom = [...cfg.collectCoverageFrom, '!**/*.(story|stories).tsx'];
cfg.coverageReporters = [...cfg.coverageReporters, ['json', { outputFile: './src/stories/.jest-test-results.json' }]];

module.exports = cfg;
21 changes: 19 additions & 2 deletions app/templates/packages/project/package.json.ejs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,11 @@
"build": "run-s clean:build build:code",
<%_ } -%>
"translate": "ts-node -P tsconfig.run.json scripts/transform.ts",
"test": "jest",
"storybook": "run-s test:coverage storybook:clean storybook:build",
"storybook:build": "build-storybook -o ./storybook -s ../../coverage/react",
"storybook:clean": "rimraf node_modules/.cache storybook",
"storybook:dev": "start-storybook -p 6006 -s ../../coverage/react",
"test": "jest --json --outputFile=src/stories/.jest-test-results.json",
"test:coverage": "yarn test --coverage",
"snowpack:dev": "dotenv -e ../../env/development.env -- snowpack dev",
"snowpack:watch": "dotenv -e ../../env/development.env -- snowpack build --watch",
Expand Down Expand Up @@ -89,6 +93,19 @@
"tailwindcss-elevation": "1.0.1",
"twin.macro": "2.3.0",
<%_ } -%>
"snowpack": "3.0.13"
"snowpack": "3.0.13",
"react-docgen-typescript": "1.21.0",
"@storybook/addon-a11y": "6.1.21",
"@storybook/addon-actions": "6.1.21",
"@storybook/addon-console": "1.2.3",
"@storybook/addon-essentials": "6.1.21",
"@storybook/addon-jest": "6.1.21",
"@storybook/addon-links": "6.1.21",
"@storybook/addon-storysource": "6.1.21",
"@storybook/addons": "6.1.21",
"@storybook/preset-create-react-app": "3.1.7",
"@storybook/react": "6.1.21",
"storybook-addon-performance": "0.14.0",
"storybook-addon-turbo-build": "1.0.0-beta.0"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import type { Meta, Story } from '@storybook/react';
import React from 'react';
import Button, { ButtonProps } from './button.component';

export default {
title: 'Components/Button',
component: Button,
parameters: {
jest: ['__tests__/button.spec.tsx']
},
argTypes: {
onClick: {
table: {
category: 'Events'
}
}
}
} as Meta;

const Template: Story<ButtonProps> = (args) => <Button {...args} />;

export const defaultView = Template.bind({});
defaultView.args = {
children: 'Button'
};

export const Disabled = Template.bind({});
Disabled.args = {
disabled: true,
children: 'Button'
};
16 changes: 16 additions & 0 deletions app/templates/packages/project/src/i18n/i18n.provider.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import React, { PropsWithChildren, useEffect } from 'react';
import { I18nextProvider } from 'react-i18next';
import { Locale, useTranslation } from './i18n.hooks';

export type I18nProviderProps = Required<PropsWithChildren<unknown>> & {
locale: Locale;
};

export function I18nProvider({ children, locale }: I18nProviderProps) {
const { i18n, changeLanguage } = useTranslation();
useEffect(() => {
void changeLanguage(locale);
}, [changeLanguage, locale]);

return <I18nextProvider i18n={i18n}>{children}</I18nextProvider>;
}
1 change: 1 addition & 0 deletions app/templates/packages/project/src/i18n/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
/* istanbul ignore file */
export * from './i18n.hooks';
export * from './i18n.provider';
Empty file.
Loading