Portada proyecto librería UI

UI Library – Proyecto con Webpack – Parte #1

Última actualización:

¿Qué vamos a construir? 

Una pequeña librería UI (en inglés, User Interface library), es decir, un conjunto de componentes re-utilizables en cualquier proyecto, como pueden ser:

  • tooltips
  • snackbars
  • dropdowns
  • pestañas (tabs)

Es una versión propia de UI libraries como Bootstrap, que cuenta con ese tipo de componentes re-utilizables. Aquí tienes el resultado final. 🤸‍♀️

Requisitos y consideraciones previas

Vamos a construir este proyecto usando Webpack, así que necesitarás nociones sobre el tema. Por suerte, aquí tienes una guía completa de Webpack y Babel. En esa guía verás cómo instalar NPM y NodeJS, necesarios para este proyecto. Construiremos este proyecto con todo lo explicado en la guía, así que no te la pierdas. 🤗

También necesitarás saber lo básico sobre JavaScript y Programación orientada a objetos (POO 💩).

Partiremos de un Webpack boilerplate que puedes encontrar en esta tercera parte de la serie sobre Babel y Webpack. Dicho esto, ¡empezamos! 🤠

Estructura inicial de nuestra app

Utilizaré el boilerplate de arriba, así que los pasos a seguir serían:

1. Crear una carpeta en el lugar que tú elijas de tu pc, llamada, en mi caso, ui-library-project.

2. Descargar el boilerplate de arriba y volcar todo el contenido del archivo ZIP dentro de la carpeta ui-library-project. Eso nos debería dejar la siguiente estructura de archivos:

UI-LIBRARY-PROJECT
  • dist
    • assets
      • bundle.js
    • index.html
  • src
    • index.js
  • .gitignore
  • package-lock.json
  • package.json
  • webpack.config.js

3. Abrimos el proyecto en nuestro IDE (vsCode en mi caso) y navegamos hasta la carpeta raíz (en inglés, root) de nuestro proyecto. Ahí hacemos npm install para instalar los node_modules listados en el package.json. 

Es posible que al instalar NPM encuentre algunas vulnerabilidades y así nos lo hará saber con un mensaje tipo:

added 649 packages from 328 contributors and audited 12205 packages in 11.117s
found 307 vulnerabilities (1 moderate, 306 high)

Pero a continuación nos da la solución, ya que sólo tendremos que usar el comando npm audit fix. 😌

Para desarrollar nuestro proyecto en modo de desarrollo (en inglés, develop mode), usamos el comando npm run serve. Esto nos proporcionará un un servidor local de desarrollo (en inglés, local development server). Es la línea donde pone:

i ï½¢wdsï½£: Project is running at http://localhost:8080/

4. Probamos si todo funciona bien, yendo al index.js y escribiendo un console.log, que deberíamos ver en la consola de nuestro navegador.

console.log('this is a test');

5. Creamos una carpeta dentro de src llamada uique contendrá todos los archivos JS de nuestra app. Posteriormente importaremos los módulos que necesitemos dentro del index.js.

Organización de los archivos CSS usando CSS loaders

Incluiremos nuestros archivos CSS (que darán estilo a nuestros componentes) dentro de la carpeta ui, pero para eso necesitaremos procesar los CSS usando el sistema de módulos de Webpack y un CSS loaderEmpezaremos añadiendo código en el webpack.config.js.

Observarás que de momento solo tenemos una regla (en inglés, rule):

    rules: [{
      test: /\.js$/,
      exclude: /node_modules/,
      use: {
        loader: 'babel-loader',
        options: {
          presets: ['@babel/preset-env']
        }
      }
    }]

Así que debemos crear otra regla que procesará nuestros archivos CSS a través de unos CSS loaders.

1. Instalamos un par de loaders relacionados con CSS. Para eso:

  • cancelamos el local server con el comando Ctrl+c
  • los instalamos y guardamos como dev dependencies con el comando npm install css-loader style-loader --save-dev.
El CSS loader ayudará a Webpack a recopilar archivos CSS cuando los importemos, mientras que el style loader toma ese CSS y lo integra a nuestro HTML.

2. Una vez instalados, creamos las reglas que se le van a aplicar (en el webpack.config.js).

La primera regla es un objeto, así que creamos una segunda regla usando el mismo patrón. 

La propiedad test utiliza el mismo sistema en su RegEx que para la regla anterior, pero en lugar de buscar archivos .js, buscará archivos .css.

En cuanto a la propiedad use, sólo necesitamos indicarle qué loaders queremos usar, así que no vamos a usar ningún preset.

  • Utilizamos un array para indicar los loaders a usar. El orden es vital aquí, ya que Webpack lee esta línea de derecha a izquierda. Por tanto, queremos que primero recopile los archivos CSS y que después los añada a nuestro documento HTML.
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        use: {
          loader: 'babel-loader',
          options: {
            presets: ['@babel/preset-env']
          }
        }
      },
      {
        test: /\.css$/,
        use: ['style-loader', 'css-loader']
      }
    ]

Con esto hecho, ya podemos lanzar nuestro local server de nuevo, usando npm run serve. 🚀

Para probar que funciona, creamos un archivo CSS dentro de la carpeta ui, llamado test.css. Dentro añadimos simplemente un color de fondo para el <body>.

body {
    background: blanchedalmond;
}

Si ahora guardas y vas a tu navegador, verás que el color aún no se aplica, ya que no hemos importado el archivo CSS en ningún sitio.

3. Vamos a importarlo en el index.jsal principio del archivo.

import './ui/test.css';

¡Y voilà! Si ahora guardas, ya deberías ver en tu navegador que el fondo ha cambiado de color. ✌

Vamos a organizar mejor la estructura de carpetas, creando una nueva carpeta dentro de ui, donde irán todos nuestros CSS. La llamamos styles. 👩‍🎨

Creando tooltips

Hecho esto, vamos a crear nuestro primer componente re-utilizable: un tooltip. Ya sabes, mensajitos que salen cuando pasas el ratón por encima de un texto o una imagen para aportarte información útil. 🙋‍♀️

1. En el archivo index.html, creamos un pequeño inline style dentro del <head>, un container que englobará toda nuestra app.

2. Creamos el HTML del tooltipque consistirá en un párrafo con un <span> dentro que acotará el texto al que le aplicaremos el tooltip. El tooltip será visible cuando pasemos por encima del texto del <span>, una vez hayamos configurado su funcionalidad. El mensaje contenido en el tooltip estará dentro del <span> también.

<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title></title>
  <style>
    .container {
      margin: 5% 28%;
    }
  </style>
</head>
<body>
  <div class="container">
    <!-- tooltip -->
    <p>Lorem ipsum dolor sit amet consectetur, adipisicing elit.
      <span class="tooltip" data-message="I'm a tooltip!">¿Qué soy?</span>
      Possimus optio dolor, architecto incidunt aliquid accusantium! Necessitatibus ratione itaque officia quia!</p>
  </div>
  <script src="assets/bundle.js"></script>
</body>
</html>

3. Creamos un archivo JS dentro de la carpeta ui, llamado tooltip.js. Ahí es donde vamos a crear la lógica de nuestro componente. Utilizaremos clases y arrow functionsentre otras cosas, y para cada componente crearemos una clase.

4. Creamos la clase Tooltip, que recibirá un element como parámetro. Ese element es el elemento al que queremos aplicarle el tooltip. En nuestro caso será la etiqueta <span>, que abarca el texto "¿Qué soy?". El objetivo es que el tooltip aparezca cuando pasemos el ratón por encima de ese texto.

Cuando creemos una instance de la clase Tooltip, le pasaremos como argumento esa etiqueta <span>.

5. Creamos una propiedad que contenga el mensaje del tooltip ("I'm a tooltip!"), y accedemos a ella a través del método getAttribute(). Con esto terminamos de configurar el constructor.

class Tooltip {
    constructor(element) {
        this.element = element;
        this.message = element.getAttribute('data-message');
    }
}

6. Creamos un método dentro de la clase llamado init(), que utilizaremos cuando queramos inicializar el componente. Dentro de esta función, creamos una variable que almacene el tooltip en sí mismo. Lo almacenamos dentro de un <div> recién creado. Lo estándar es que un tooltip tenga apariencia de bocadillo de cómic o de burbuja, así que llamamos a la variable bubble

7. Le añadimos una clase de CSS que aun no existe, llamada también bubble. Le daremos estilo luego.

8. Definimos el contenido que tendrá esa bubble, que será el contenido del this.message.

Con la variable bubble ya apañada, ahora tenemos que adjuntarla (en inglés, appendal DOM, concretamente a la etiqueta <span>.

    init() {
        const bubble = document.createElement('div');
        bubble.classList.add('bubble');
        bubble.textContent = this.message;
        this.element.appendChild(bubble);
    }

9. Exportamos la clase desde el tooltip.js

export { Tooltip as default }; 

y la importamos en el index.js para poder usarla. Si tenías algo en este archivo sobre las pruebas que hemos hecho anteriormente, bórralo.

import Tooltip from './ui/tooltip';

Llegamos al momento que hemos adelantado arriba: crear una instance de la clase Tooltip.

10. Seleccionamos el <span> con un querySelector.

// create a tooltip
const tooltip = document.querySelector('.tooltip');

Una vez seleccionado el <span>, lo utilizamos para crear una instance de la clase Tooltip, pasándoselo como argumento, porque recordemos que el Tooltip espera un element.

11. Inicializamos el componente usando el método init().

const tooltip = new Tooltip(document.querySelector('.tooltip'));
tooltip.init();

Si ahora guardas y vas a tu navegador, podrás ver el siguiente texto:

Lorem ipsum dolor sit amet consectetur, adipisicing elit. ¿Qué soy?
I'm a tooltip!
Possimus optio dolor, architecto incidunt aliquid accusantium! Necessitatibus ratione itaque officia quia!

Efectivamente, aún no tiene funcionalidad, ya que vemos el texto del tooltip como si fuera parte del párrafo. Así que aún nos quedan dos pasos: darle una capa de chapa y pintura y hacer que aparezca únicamente cuando pasamos el ratón por encima de las palabras "¿Qué soy?".

Para hacerlo visible o invisible, utilizaremos una clase de CSS a la que llamaremos active, que configuraremos despuésEste paso lo haremos en el tooltip.js, dentro del método init().

12. Añadimos un eventListener al <span> y lo vinculamos al evento mouseenterDentro de la callback function le añadimos la clase active al bubble. 

13. Implementamos el caso contrario (cuando quitamos el ratón de esa zona), así que usamos el evento mouseleave. 

        this.element.addEventListener('mouseenter', () => {
            bubble.classList.add('active');
        });

        this.element.addEventListener('mouseleave', () => {
            bubble.classList.remove('active');
        });

Para probar si esto funciona, podemos abrir la consola y observar cómo la clase active se añade y elimina cada vez que pasamos el ratón por encima de "¿Qué soy?". 👍

14. Creamos un archivo CSS dentro de styles, que contendrá el CSS del componente tooltipLo llamamos tooltip.css

.tooltip {
    position: relative;
    display: inline-block;
    color: coral;
    border-bottom: 1px dotted coral;
    cursor: help;
}

.bubble {
    position: absolute;
    visibility: hidden;
    width: 150px;
    background-color: coral;
    color: #fff;
    text-align: center;
    border-radius: 10px;
    padding: 5px 0;
    bottom: 120%;
    left: 50%;
    margin-left: -75px;
    opacity: 0;
    transition: opacity 0.3s;
}

.bubble.active {
    visibility: visible;
    opacity: 1;
}

.bubble::after {
    content: "";
    position: absolute;
    top: 100%;
    left: 50%;
    margin-left: -4px;
    border-width: 4px;
    border-style: solid;
    border-color: transparent;
    border-top-color: coral;
}

15. Importamos el tooltip.css en el tooltip.js, al principio.

import './styles/tooltip.css';

¡Nuestro tooltip está listo! 😮

tooltip final

Creando dropdowns

Los dropdowns los podemos encontrar en cualquier texto que se despliegue, ya sea un menú con varias opciones o un texto más largo con varias secciones. Si podemos colapsar y expandir las secciones a base de click, estamos ante un dropdownY ahora vamos a aprender a crearlos.

Haremos un par de secciones que podrán colapsarse mostrando solo el título o expandirse y mostrar un párrafo de contenido.

1. Creamos el esqueleto HTML en el index.html, debajo del código HTML del tooltip, pero dentro de la clase container.

  <!-- dropdowns -->
  <h3>Minion products</h3>
  <div class="dropdown">
    <div class="trigger">Talking teddy bear</div>
    <div class="content">
      <p>Lorem ipsum dolor sit amet consectetur adipisicing elit. Omnis doloribus assumenda optio. Adipisci, modi sint!
      </p>
    </div>
  </div>
  <div class="dropdown">
    <div class="trigger">Minion Machine to rule the World</div>
    <div class="content">
      <p>Lorem ipsum damet conseolor sit amet cosectetur adipisicg elit. Omm ipsum dolor sit amet coetur adipisicing nis
        doloribptio. Adipisci, modi si</p>
    </div>
  </div>

Los trigger son el bloque de texto que desplegará el texto del párrafo content al hacer click sobre ellos. Ya deberías ver el contenido en tu navegador si guardas los cambios. De momento todo es visible, tanto el título como el contenido. Pero queremos que el contenido solo sea visible al hacer click sobre el título.

2. Creamos un nuevo archivo JS a la altura de tooltip.js, llamado dropdown.js. Ahí es donde crearemos una clase de JS para el dropdown. 

El constructor espera un containeres decir, uno de estos bloques:

  <div class="dropdown">
    <div class="trigger">Minion Machine to rule the World</div>
    <div class="content">
      <p>Lorem ipsum damet conseolor sit amet cosectetur adipisicg elit. Omm ipsum dolor sit amet coetur adipisicing nis
        doloribptio. Adipisci, modi si</p>
    </div>
  </div>

👉 Para este proyecto vamos a crear un par de instances de la clase Dropdown que vamos a construir a continuación en el dropdown.js.

3. En el constructor creamos las propiedades container trigger, al cual podemos acceder a través del DOM. Lo mismo hacemos para otra propiedad a la que llamamos content.

4. Creamos un método llamado init() para inicializar la clase. Dentro de este método configuraremos una opción para mostrar u ocultar el content cuando el usuario haga click en el triggerPara eso, añadimos un event listener al trigger y lo vinculamos a un evento click.

Para añadirle esa capacidad de ocultarse y mostrarse utilizamos el método toggle() vinculado a una clase de CSS llamada activeque aún está por configurar. Aplicamos el mismo comportamiento al content, para que se oculte o muestre según el usuario haga click en el triggerEn realidad el hecho de aplicarle la clase active al trigger es más para poder saber cuándo mostrar una flecha que indique que el contenido está desplegado o no 🤓. Veremos eso más adelante.

5. Exportamos la clase como default.

class Dropdown {
    constructor(container) {
        this.container = container;
        this.trigger = container.querySelector('.trigger');
        this.content = container.querySelector('.content');
    }
    init() {
        this.trigger.addEventListener('click', () => {
            this.trigger.classList.toggle('active');
            this.content.classList.toggle('active');
        })
    }
}

export { Dropdown as default };

6. Importamos el módulo Dropdown en el index.js, igual que hemos hecho con el módulo Tooltip.

Queremos crear dos instances de la clase Dropdown, así que primero obtenemos una referencia a través del DOM. Para eso utilizamos el querySelectorAllque nos devuelve una nodeList sobre la que podemos iterar. Dentro de la callback function del loop forEach es donde creamos la instance de la clase, guardada en una variable llamada instance. 

A continuación ya podemos inicializarla con el método init().

Si ahora guardas y observas la consola de tu navegador, verás que la clase active se alterna cada vez que haces click en uno de los títulos "Talking teddy bear" o "Minion Machine to rule the World". ¡Chachi! 😸

7. Creamos un archivo CSS a la altura de tooltip.css, llamado dropdown.css, donde configuraremos los estilos de nuestros dropdowns. 

.dropdown .trigger {
    border-bottom: 1px solid #ddd;
    padding: 10px;
    position: relative;
    cursor: pointer;
}

.dropdown .trigger::after {
    content: '>';
    color: coral;
    display: inline-block;
    font-weight: bold;
    position: absolute;
    right: 15px;
    transform: rotate(90deg) scale(1, 2);
    transition: transform 0.3s;
}

.dropdown .trigger.active::after {
    transform: rotate(-90deg) scale(1, 2);
}

.dropdown .content {
    display: none;
}

.dropdown .content.active {
    display: block;
}

8. Importamos el dropdown.css en el dropdown.js, al principio del archivo.

import './styles/dropdown.css';

Si guardas, ya deberías ver los cambios en tu navegador, con un par de "secciones" que se despliegan y se vuelven a plegar al hacer click sobre los títulos. 👌

THE END!

¡Y hasta aquí esta primera parte de nuestra librería UI! Espero que hayas aprendido algo nuevo 😊.  Si te queda alguna duda, ¡nos vemos en los comentarios! Si quieres seguir aprendiendo, nos vemos en la parte #2.

Y si crees que este post puede serle útil a alguien, ¡compártelo!

Otros artículos que pueden interesarte

Días del 353 al 386
Objetivos versus realidad Y nuevamente, llegó otro día clave. Llegó…y pasó. El pasado 4 de marzo este Reto Computer Geek[...]
Angular: Entendiendo la Directiva ngModel
Angular es un framework que nos permite, entre otras cosas, añadir contenido dinámico a nuestros archivos HTML. Una de las formas[...]
Pipes en Angular | Guía completa
¿Qué son los pipes?Los pipes son una herramienta de Angular que nos permite transformar visualmente la información, por ejemplo, cambiar un[...]
Si crees que este post puede serle útil a alguien, por favor, ¡compártelo!:

Deja un comentario

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.

Como toda web legal que se precie, utilizamos cookies para asegurar que damos la mejor experiencia al usuario en nuestro sitio web. Si continúas utilizando este sitio asumiremos que estás de acuerdo. más información

Los ajustes de cookies de esta web están configurados para "permitir cookies" y así ofrecerte la mejor experiencia de navegación posible. Si sigues utilizando esta web sin cambiar tus ajustes de cookies o haces clic en "Aceptar" estarás dando tu consentimiento a esto.

Cerrar