Portfolio
React
TailwindCSS

Actualizando Portafolio [Parte 2]

Aquí explicaré la configuración inicial de mi sitio web de portafolio con React, TailwindCSS, Jest, React Test Library y ESlint.

Ivan Robles

6 min read
Actualizando Portafolio [Parte 2]

En el post pasado les hable acerca del proceso de diseño, hoy vamos a brincarle al desarrollo.

Bueno, casi. Primero vamos a hacer el bootstraping, el setup, las configuraciones iniciales para que nuestro proyecto pueda correr y para así sentarnos a hacer código como dios manda.

Esta es una serie de posts, describiendo los procesos que llevé acabo para la creación de mi portafolio, el cual se encuentra en: https://sharmaz.github.io/me/

[Parte 1, El diseño]
[Parte 2, El setup] 👈️ Aquí estamos
[Parte 3, El desarrollo]
[Parte 4, Los workflows]
[Parte 5, La migración a typescript]

Así esta la cosa, este proyecto lo pensé en dos aplicaciones:

  1. El Frontend que consume el contenido con peticiones HTTP y lo pinta o renderea con un diseño de mi agrado, alojado en github pages asociado a la ruta que siempre ha dirigido a mi portafolio.
  2. El Backend, una aplicación donde pueda alojar y actualizar la información de mi carrera como desarrollador a través del tiempo, la información es almacenada en una base de datos y esos datos se van a exponer en una API para ser consumida por la aplicación Frontend.

Aunque puedo decir que tengo habilidades de Fullstack Javascript, mi experiencia como desarrollador se inclina al Frontend, es por eso que decidí comenzar por ese lado.

Ademas, es posible comenzar a trabajar aunque no tengamos los datos desde el Backend.

Ambos proyectos fueron creados desde cero (from scratch), o sea sin frameworks como next.js, gatsby o incluso create react app.

Y es por eso que le vamos a dar a la configuración y setupeo.

El Setup 🧑‍🔧⚙️

En este proyecto se usan las siguientes librerías:

  • React, pues porque React.
  • TailwindCSS, pa’l Style no chuntaro… bueno tal vez un poco.
  • Jest y React Testing Library, pa’ las pruebas o el testeo.
  • ESlint, pal´style de código. Este si no es chuntaro, se los prometo.
  • Typescript, este lo deje al final para hacer un port de lo desarrollado con Javascript.

Voy a describir el proceso en esta serie de pasos de lo que buscamos y como obtenerlos:

El Repo ☁️ 🗂️

Lo que primero es ir a Github, crear un repositorio, clonarlo con:

git clone [repo_link]

Luego inicializar el proyecto de Javascript con:

npm init -y

Por lo regular no agrego la opción -y y configuro lo que pueda como el nombre del proyecto, la licencia etc.

Configurar Webpack para correr un “Hola Mundo” con React 👋 🌎

Install Party!! vamos por las dependencias de React, Webpack y Babel:

npm install --save-dev @babel/core babel-loader @babel/cli @babel/preset-env @babel/preset-react

Hay que crear el archivo de configuración de Babel  .babelrc y a agregar lo siguientes presets y plugins:

{
  "presets": [
      "@babel/preset-env",
      "@babel/preset-react"
  ],
  "plugins": [
      "@babel/plugin-transform-runtime"
  ]
}

Instalamos las dependencias referentes a Webpack junto con el html-webpack-plugin:

npm install --save-dev webpack webpack-cli webpack-dev-server html-webpack-plugin

Instalamos dependencias de React:

npm install react react-dom

Hay que crear el archivo de configuración webpack.config.js:

const path = require("path");
const HtmlWebPackPlugin = require("html-webpack-plugin");
const htmlPlugin = new HtmlWebPackPlugin({
 template: "./src/index.html",
 filename: "index.html"
});

module.exports = {
    mode: "development", 
    entry: "./src/index.js", 
    output: {
        path: path.resolve(__dirname, "dist"),
        filename: "index.js"
    },
    target: "web",
    devServer: {
        port: "5000",
        static: ["./dist"],
        open: true,
        hot: true ,
        liveReload: true
    },
    resolve: {
        extensions: ['.js','.jsx','.json'] 
    },
    module:{
        rules: [
            {
                test: /\\.(js|jsx)$/,
                exclude: /node_modules/,
                use:  'babel-loader'
            }
        ]
    },
    plugins: [htmlPlugin]
}

Lo básico es agregar un entry point y un output, las opciones para devServer como el puerto donde va a correr, las extensiones que vamos a transpilar con el babel-loader y especificar los plugins como en este caso el html-webpack-plugin.

Con esto listo ya podemos crear la carpeta /src con los archivos index.html e index.js para nuestro hola mundo.

index.html:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
  <div id="root"></div>
</body>
</html>

index.js:

import React from 'react';
import { createRoot } from 'react-dom/client';

const App = () => (
  <h1>
    Hola Mundo!
  </h1>
);

export default App;

const root = createRoot(document.getElementById('root'));
root.render(<App />);

Es momento de ir al package.json a modificar y agregar los scripts que van a correr Webpack para correr el proyecto y para crear un build para producción:

"scripts": {
    "start": "webpack-dev-server",
    "build": "webpack",
  },

Configuración de ESlint 💅💻

Ya se la saben amigos, a instalar dependencias:

npm install eslint --save-dev

Aquí hay una ventaja, gracias a que en ESlint hicieron una aplicación para CLI, es posible configurar  paso a paso en la terminal:

npm init @eslint/config

Una vez indicándole al CLI las opciones deseadas para nuestro proyecto, nos va a crear un archivo de configuración que va a contener algo como esto:

{
  "env": {
    "browser": true,
    "es2021": true
  },
  "extends": [
    "plugin:react/recommended",
    "airbnb"
  ],
  "overrides": [
  ],
  "parserOptions": {
    "ecmaVersion": "latest",
    "sourceType": "module"
  },
  "plugins": [
    "react"
  ],
  "rules": {
    "react/jsx-filename-extension": [1, { "extensions": [".js", ".jsx"] }],
    "react/function-component-definition": [2, {
      "namedComponents": "arrow-function",
      "unnamedComponents": "arrow-function"
    }]
  }
}

Aunque ya con esto nuestro editor VS Code ya nos va a comenzar a regañar gracias a ESlint, hay que agregar un Script en nuestro package.json para poder correrlo desde linea de comandos:

"scripts": {
    "lint": "eslint ./src --ext .js"
  },

Configuracion de Pruebas (Jest y React Testing Library) 🐙 🃏

Le damos a la instalación de nuestras dependencias:

React Testing Library:

npm install --save-dev @testing-library/react @testing-library/jest-dom

Jest:

npm install --save-dev jest jest-environment-jsdom

Creamos el archivo de configuración para Jest jest.config.js :

module.exports = {
  collectCoverage: true,
  collectCoverageFrom: ['src/**/*.{js,jsx}'],
  coverageDirectory: 'coverage',
  testEnvironment: 'jsdom',
  setupFilesAfterEnv: ['<rootDir>/jest.setup.js'], // 👈️ Ojo aqui
};

En la propiedad setupFilesAfterEnv ponemos la dirección de jest.setup.js que va a contener el import a React Testing Library:

import '@testing-library/jest-dom';

Para integrar ESlint, vamos a agregar algunas dependencias:

npm install --save-dev eslint-plugin-jest

Y en el archivo .eslintrc.json hay que agregar el ESlint plugin de Jest:

"extends": [
    "plugin:react/recommended",
    "plugin:jest/recommended", // 👈️ Este de aqui
    "airbnb"
  ],

Vamos a crear los Scripts correspondientes para correr los test y para que nos devuelva el coverage en nuestro package.json:

"scripts": {
    "start": "webpack-dev-server",
    "build": "webpack",
    "lint": "eslint ./src --ext .js",
    "test": "jest",
    "coverage": "jest --coverage"
  },

Con esto ya podemos crear nuestra primera prueba. Mis pruebas las hice en la carpeta /src/__test__/ , vamos a testear que nuestro hola mundo en App.js se este rendereando correctamente:

import { render, screen } from '@testing-library/react';
import React from 'react';
import App from '../App';

describe('App tests', () => {
  test('should contains the heading 1', () => {
    render(<App />);
    const heading = screen.getByText('Hola Mundo!');
    expect(heading).toBeInTheDocument();
  });
});

Corremos las pruebas con npm run test y debería salir todo en verde, o sea que pasamos las pruebas correctamente.

Running Tests

Configuración de TailwindCSS 🌬️🪁

A instalar dependencias:

Tailwind:

npm install tailwindcss autoprefixer --save-dev

Creamos el archivo tailwind.config.cjs con lo siguiente:

module.exports = {
  content: ['./src/**/*.{js,jsx}', './dist/index.html'],
  theme: {
    extend: {
      colors: {
        primary: '#282C34',
      },
    },
  },
  plugins: [],
};

Creamos el archivo de configuración de PostCSS postcss.config.cjs:

const tailwindcss = require('tailwindcss');
const autoprefixer = require('autoprefixer');

module.exports = {
  plugins: [tailwindcss('./tailwind.config.cjs'), autoprefixer],
};

Creamos el archivo /src/app.css para tener los estilos con TailwindCSS:

@tailwind base;
@tailwind components;
@tailwind utilities;

Para después importar esos estilos en nuestra App.js:

import React from 'react';
import './app.css';

const App = () => (
  <h1 className="text-primary font-bold">
    Hola Mundo!
  </h1>
);

export default App;

Pero nos vamos a ir pa’ tras porque no van a jalar si no hacemos la configuración de webpack, hay que instalar los Loaders correspondientes:

npm install style-loader css-loader postcss-loader

Y luego integrarlas a la configuración de webpack.config.js:

module: {
    rules: [
      ...
      {
        test: /\\.css$/i,
        use: ['style-loader', 'css-loader', 'postcss-loader'], // 👈️ este bloque
      },
    ],
  },

Volviendo a correr el proyecto los estilos con TailwindCSS deberían funcionar.

Conclusión

Instalar - Configurar, el señor Miyagi estaría orgulloso de nosotros, a veces la vida se trata de instalar dependencias y conectarlas en archivos "rc" como si fueran tuberías.

Instalar - Configurar

Ahora sí, una vez que teniendo todo funcionando correctamente, podemos comenzar a desarrollar nuestras secciones y componentes del sitio, pero de eso hablaremos en el siguiente post.

Nos vemos luego!!