DEV Community

Cover image for React Typescript con StoryBook
Edison Sanchez
Edison Sanchez

Posted on

React Typescript con StoryBook

Como primer paso después de hacer un CRA (create-react-app), instalo Storybook y Bit; los dos me permiten tener mi documentación, toolbox, y sincronización de mis componentes a través de todos mis proyectos.

Recientemente estoy migrando mis apps y las nuevas que voy realizando en React con Typescript. A continuación los pasos para configurar correctamente (según mi manera de ver) React con Typescript y Storybook.

1. Crear el Proyecto con Soporte de Typescript.

Supongamos que mi proyecto se llama MiProyecto:

npx create-react-app MiProyecto --template typescript
Enter fullscreen mode Exit fullscreen mode

Con esto ya tendré mi proyecto configurado en Typescript, pero aun faltan algunas configuraciones para que en VSCode trabaje de la manera correcta:

Instalar dependencias

A continuación estas son todas las dependencias para una integración correcta de Typescript en el proyecto creado:

npm install -D @typescript-eslint/eslint-plugin @typescript-eslint/parser babel-eslint eslint eslint-config-airbnb  eslint-config-prettier eslint-plugin-import  eslint-plugin-jest eslint-plugin-jsx-a11y eslint-plugin-prettier eslint-plugin-react  husky lint-staged prettier react-test-renderer require-context.macro tslint tslint-config-prettier tslint-plugin-prettier tslint-react 
Enter fullscreen mode Exit fullscreen mode

Aquí hay algunas instaladas preparándonos para Storybook. Debemos crear algunos archivos para la configuración del linter en el root del proyecto:

.env

SKIP_PREFLIGHT_CHECK=true
Enter fullscreen mode Exit fullscreen mode

.eslintrc.js

module.exports = {
  parser: '@typescript-eslint/parser', // Specifies the ESLint parser
  extends: [
    'plugin:react/recommended', // Uses the recommended rules from @eslint-plugin-react
    'plugin:@typescript-eslint/recommended', // Uses the recommended rules from the @typescript-eslint/eslint-plugin
    'prettier/@typescript-eslint', // Uses eslint-config-prettier to disable ESLint rules from @typescript-eslint/eslint-plugin that would conflict with prettier
    'plugin:prettier/recommended', // Enables eslint-plugin-prettier and displays prettier errors as ESLint errors. Make sure this is always the last configuration in the extends array.
  ],
  parserOptions: {
    ecmaVersion: 2020, // Allows for the parsing of modern ECMAScript features
    sourceType: 'module', // Allows for the use of imports
    ecmaFeatures: {
      jsx: true, // Allows for the parsing of JSX
    },
  },
  rules: {
    // Place to specify ESLint rules. Can be used to overwrite rules specified from the extended configs
    // e.g. "@typescript-eslint/explicit-function-return-type": "off",
  },
  settings: {
    react: {
      version: 'detect', // Tells eslint-plugin-react to automatically detect the version of React to use
    },
  },
};
Enter fullscreen mode Exit fullscreen mode

.prettierrc

{
  "printWidth": 120,
  "singleQuote": true,
  "trailingComma": "es5",
  "tabWidth": 2
}
Enter fullscreen mode Exit fullscreen mode

tsconfig.json

{
  "compilerOptions": {
    "target": "ES2016",
    "lib": ["dom", "dom.iterable", "esnext"],
    "allowJs": true, // Allow JavaScript files to be compiled
    "skipLibCheck": true, // Skip type checking of all declaration files
    "esModuleInterop": true, // Disables namespace imports (import * as fs from "fs") and enables CJS/AMD/UMD style imports (import fs from "fs")
    "allowSyntheticDefaultImports": true, // Allow default imports from modules with no default export
    "strict": true, // Enable all strict type checking options
    "forceConsistentCasingInFileNames": true, // Disallow inconsistently-cased references to the same file.
    "module": "esnext", // Specify module code generation
    "moduleResolution": "node", // Resolve modules using Node.js style
    "isolatedModules": true,
    "resolveJsonModule": true, // Include modules imported with .json extension
    "noEmit": true, // Do not emit output (meaning do not compile code, only perform type checking)
    "jsx": "react", // Support JSX in .tsx files
    "sourceMap": true, // Generate corrresponding .map file
    "declaration": true, // Generate corresponding .d.ts file
    "noUnusedLocals": true, // Report errors on unused locals
    "noUnusedParameters": true, // Report errors on unused parameters
    "experimentalDecorators": true, // Enables experimental support for ES decorators
    "noFallthroughCasesInSwitch": true // Report errors for fallthrough cases in switch statement
  },
  "include": [
    "src/**/*" // *** The files TypeScript should type check ***
  ],
  "exclude": ["node_modules", "build"], // *** The files to not type check ***
  "plugins": [{ "name": "typescript-tslint-plugin" }]
}
Enter fullscreen mode Exit fullscreen mode

tslint.json

{
  "extends": ["tslint:recommended", "tslint-react", "tslint-config-prettier"],
  "rulesDirectory": ["tslint-plugin-prettier"],
  "rules": {
    "prettier": true,
    "interface-name": false
  },
  "linterOptions": {
    "exclude": ["config/**/*.js", "node_modules/**/*.ts", "coverage/lcov-report/*.js"]
  }
}
Enter fullscreen mode Exit fullscreen mode

y en el Package debemos configurar:

scripts: {
...
    "lint-ts": "tslint -c tslint.json 'src/**/*.{ts,tsx}'",
    "lint-js": "eslint 'src/**/*.{js,jsx}' --quiet --fix",
    "lint": "tslint -c tslint.json src/**/*.{ts,tsx} --fix --format verbose",
...
},
...
"husky": {
    "hooks": {
      "pre-commit": "export CI=true && yarn build && lint-staged && yarn test",
      "pre-push": "export CI=true && yarn build && lint-staged && yarn test"
    }
  },
  "lint-staged": {
    "*.{ts,tsx}": [
      "tslint -c tslint.json"
    ],
    "*.{js,jsx}": [
      "eslint --fix"
    ]
  }
...
Enter fullscreen mode Exit fullscreen mode

Ejecutamos los linters para verificar que no tenemos errores, en caso de encontrarlos corregirlos.

npm run lint-ts
npm run lint-js
npm run lint
Enter fullscreen mode Exit fullscreen mode

2. Instalar Storybook

Realizamos la instalación de Storybook, aqui tambien estan los addons que he empleado para mi instalación en este "boiler-plate" (importante tener instalado el cli de Storybook):

npx -p @storybook/cli sb init --story-format=csf-ts
npm install -D @storybook/addon-actions @storybook/addon-docs @storybook/addon-knobs @storybook/addon-links @storybook/addon-storyshots @storybook/preset-create-react-app @storybook/react @types/storybook__addon-knobs @types/storybook__addon-storyshots @types/storybook__reac 
Enter fullscreen mode Exit fullscreen mode

Creamos un folder que llamaremos .storybook. En el crearemos 4 archivos:

addon.ts

import '@storybook/addon-actions/register';
import '@storybook/addon-links/register';
import '@storybook/addon-docs/register';
import '@storybook/addon-knobs/register';
Enter fullscreen mode Exit fullscreen mode

config.ts

import { configure } from '@storybook/react';
import requireContext from 'require-context.macro';
const req = requireContext('../src', true, /\.stories\.tsx$/);
function loadStories() {
  req.keys().forEach(req);
}
configure(loadStories, module);
Enter fullscreen mode Exit fullscreen mode

main.ts

module.exports = {
  stories: ['../src/**/*.stories.(ts|tsx|js|jsx)'],
  addons: [
    '@storybook/addon-actions',
    '@storybook/addon-links',
    '@storybook/addon-knobs',
    '@storybook/preset-create-react-app',
    {
      name: '@storybook/addon-docs',
      options: {
        configureJSX: true,
      },
    },
  ],
};
Enter fullscreen mode Exit fullscreen mode

webpack.config.ts

module.exports = ({ config, mode }) => {
  config.module.rules.push({
    test: /\.(ts|tsx)$/,
    loader: require.resolve('babel-loader'),
    options: {
      presets: [['react-app', { flow: false, typescript: true }]],
    },
  });
  config.resolve.extensions.push('.ts', '.tsx');
  return config;
};
Enter fullscreen mode Exit fullscreen mode

y en nuestro package.json adicionar estas dos opciones:

...
scripts: {
...
    "storybook": "start-storybook -p 9009 -s public",
    "build-storybook": "build-storybook -s public",
...
},
...
Enter fullscreen mode Exit fullscreen mode

Ya estaria lista la configuracion; pero para probarlo corriendo debemos crear un componente, en este caso creamos un componente muy sencillo, como un boton con dos colores amarillo, y verde.

./src/components/buttons/Button/Button.tsx

import React from 'react';
// ?Button Component
export interface IProps {
  color: string;
  onClick?: (color: string) => void;
}
export default (props: IProps) => {
  const { color, onClick } = props;

  const handleClick = (): void => {
    if (onClick) onClick(color);
  };

  return (
    <button style={{ color }} onClick={handleClick}>
      Color Button
    </button>
  );
};
Enter fullscreen mode Exit fullscreen mode

Este sería nuestro componente pero para visualizarlo en StoryBook necesitamos crear el Storie para este componente:

./src/components/buttons/Button/Button.stories.tsx

import React from 'react';
import { storiesOf } from '@storybook/react';
import Button from './Button';

import { action } from '@storybook/addon-actions';
import { withKnobs, text, select } from '@storybook/addon-knobs';

storiesOf('Button', module)
  .addDecorator(withKnobs)
  .add('Amarillo', () => (
    <Button
      color={select('color', { Amarillo: 'yellow', Naranja: 'orange' }, 'yellow')}
      onClick={action('Presiono Click')}
    />
  ))
  .add('Verde', () => <Button color={text('color', 'green')} onClick={action('clicked')} />);
Enter fullscreen mode Exit fullscreen mode

Con esto ya creado, vamos a correr nuestro servidor de Storybook para visualizar el componente:

yarn storybook
Enter fullscreen mode Exit fullscreen mode

yarn storybook
En el navegador veriamos:
Storybook
Podemos cambiar el color de acuerdo a lo que configuramos, ver cuando presionamos el botón, ver todos los componentes, y mas.
StorybookStorybookStorybook
Ya estamos entonces listos con un proyecto inicial en React, con Typescript, y StoryBook.

Instalaremos Bit, y configuraremos estos componentes también en su versión con Styled Components en un próximo Post.

PS: Sigueme, comparte, comment y da Like si te sirvio este Post.

Top comments (0)