loading...
Cover image for React Typescript con StoryBook

React Typescript con StoryBook

edisonsanchez profile image Edison Sanchez ・5 min read

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

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 

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

.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
    },
  },
};

.prettierrc

{
  "printWidth": 120,
  "singleQuote": true,
  "trailingComma": "es5",
  "tabWidth": 2
}

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" }]
}

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"]
  }
}

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"
    ]
  }
...

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

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 

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';

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);

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,
      },
    },
  ],
};

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;
};

y en nuestro package.json adicionar estas dos opciones:

...
scripts: {
...
    "storybook": "start-storybook -p 9009 -s public",
    "build-storybook": "build-storybook -s public",
...
},
...

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>
  );
};

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')} />);

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

yarn storybook

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.

Posted on by:

edisonsanchez profile

Edison Sanchez

@edisonsanchez

Programming... React, React Native, JScript, AWS Serverless, Postgresql, Angular, IONIC, Python, and counting...

Discussion

markdown guide