MDC-112 Web: Cómo integrar MDC con marcos de trabajo web

1. Introducción

logo_components_color_2x_web_96dp.png

Los componentes de Material (MDC) ayudan a los desarrolladores a implementar Material Design. MDC, creado por un equipo de ingenieros y diseñadores de UX en Google, cuenta con decenas de componentes de IU atractivos y funcionales, y está disponible para Android, iOS, la Web y Flutter.material.io/develop.

MDC Web está diseñado para integrarse en cualquier framework de frontend y, al mismo tiempo, respetar los principios de Material Design. En el siguiente codelab, se te guiará para crear un componente de React, que usa MDC Web como base. Los principios que aprendiste en este codelab se pueden aplicar a cualquier framework de JavaScript.

Cómo se crea MDC Web

La capa de JavaScript de MDC Web consta de tres clases por componente: Component, Foundation y Adapter. Este patrón le brinda a MDC Web la flexibilidad para integrarse con los frameworks de frontend.

Foundation contiene la lógica empresarial que implementa Material Design. La Fundación no hace referencia a ningún elemento HTML. Esto nos permite abstraer la lógica de interacción de HTML en Adapter. Foundation tiene un adaptador.

El adaptador es una interfaz. Foundation hace referencia a la interfaz de Adapter para implementar la lógica empresarial de Material Design. Puedes implementar el adaptador en diferentes frameworks, como Angular o React. Una implementación de un adaptador interactúa con la estructura del DOM.

El Componente tiene una Fundación y su función es

  1. Implementa el adaptador con JavaScript que no sea de framework.
  2. Proporciona métodos públicos que actúen como proxy para métodos en Foundation.

Qué proporciona MDC Web

Cada paquete de MDC Web incluye un componente, un fundamento y un adaptador. Para crear una instancia de un Componente, debes pasar el elemento raíz al método del constructor del componente. El Component implementa un Adapter, que interactúa con los elementos DOM y HTML. Luego, el Componente crea una instancia de Foundation, que llama a los métodos de Adapter.

Para integrar MDC Web en un framework, debes crear tu propio componente en el lenguaje o la sintaxis de ese framework. El componente del framework implementa el adaptador de MDC Web y usa Foundation de MDC Web.

Qué compilarás

En este codelab, se muestra cómo compilar un Adapter personalizado para usar la lógica Foundation y lograr un componente de React de Material Design. En él, se abordan los temas avanzados que se encuentran en Integración en frameworks. En este codelab, React se usa como un framework de ejemplo, pero este enfoque se puede aplicar a cualquier otro framework.

En este codelab, compilarás la barra superior de la app y recrearás la página de demostración de la barra superior de la app. El diseño de la página de demostración ya está configurado para que puedas comenzar a trabajar en la barra superior de la aplicación. La barra superior de la aplicación incluirá lo siguiente:

  • Ícono de navegación
  • Elementos de acción
  • Hay 4 variantes disponibles: corta, siempre contraída, fija y prominente.

Requisitos:

  • Una versión reciente de Node.js (que se incluye con npm, un administrador de paquetes de JavaScript)
  • El código de muestra (que se descargará en el siguiente paso)
  • Conocimientos básicos de HTML, CSS, JavaScript y React

¿Cómo calificarías tu nivel de experiencia con el desarrollo web?

Principiante Intermedio Avanzado

2. Configura el entorno para desarrolladores

Descarga la app de inicio del codelab

La app de partida se encuentra en el directorio material-components-web-codelabs-master/mdc-112/starter.

… o clónalo desde GitHub

Para clonar este codelab desde GitHub, ejecuta los siguientes comandos:

git clone https://github.com/material-components/material-components-web-codelabs
cd material-components-web-codelabs/mdc-112/starter

Instala las dependencias del proyecto

Desde el directorio de partida material-components-web-codelabs/mdc-112/starter, ejecuta lo siguiente:

npm install

Verás mucha actividad y, al final, la terminal debería mostrar una instalación correcta:

22a33efc2a687408.png

Cómo ejecutar la app de inicio

En el mismo directorio, ejecuta lo siguiente:

npm start

Se iniciará el webpack-dev-server. Dirige tu navegador a http://localhost:8080/ para ver la página.

b55c66dd400cf34f.png

¡Listo! El código de partida para la página de demostración de React de la barra superior de la app debería estar ejecutándose en tu navegador. Deberías ver un muro de texto lorem ipsum, un cuadro Controls (en la parte inferior derecha) y una barra superior de la app sin terminar:

4ca3cf6d216f9290.png

Mira el código y el proyecto

Si abres el editor de código, el directorio del proyecto debería verse de la siguiente manera:

e9a3270d6a67c589.png

Abre el archivo App.js y observa el método render, que incluye el componente <TopAppBar>:

App.js

render() {
    const {isFixed, isShort, isRtl, isProminent, isAlwaysCollapsed, shouldReinit} = this.state;

    return (
      <section
        dir={isRtl ? 'rtl' : 'ltr'}
        className='mdc-typography'>
        {
          shouldReinit ? null :
          <TopAppBar
            navIcon={this.renderNavIcon()}
            short={isShort}
            prominent={isProminent}
            fixed={isFixed}
            alwaysCollapsed={isAlwaysCollapsed}
            title='Mountain View, CA'
            actionItems={this.actionItems}
          />
        }
        <div className={classnames('mdc-top-app-bar--fixed-adjust', {
          'mdc-top-app-bar--short-fixed-adjust': isShort || isAlwaysCollapsed,
          'mdc-top-app-bar--prominent-fixed-adjust': isProminent,
        })}>
          {this.renderDemoParagraphs()}
        </div>

        {this.renderControls()}
      </section>
    );
  }

Este es el punto de entrada para TopAppBar en la aplicación.

Abre el archivo TopAppBar.js, que es una clase Component de React sin procesar con un método render:

TopAppBar.js

import React from 'react';

export default class TopAppBar extends React.Component {
  render() {
    return (
      <header>
        TOP APP BAR
      </header>
    );
  }
}

3. Composición del componente

En React, el método render genera el código HTML del componente. El componente de la barra superior de la aplicación renderizará una etiqueta <header /> y se compondrá de 2 secciones principales:

  1. Ícono de navegación y sección del título
  2. Sección de íconos de acción

Si tienes preguntas sobre los elementos que componen la barra superior de la app, consulta la documentación en GitHub.

Modifica el método render() en TopAppBar.js para que se vea de la siguiente manera:

  render() {
    const {
      title,
      navIcon,
    } = this.props;

    return (
      <header
        className={this.classes}
        style={this.getMergedStyles()}
        ref={this.topAppBarElement}
      >
        <div className='mdc-top-app-bar__row'>
          <section className='mdc-top-app-bar__section mdc-top-app-bar__section--align-start'>
            {navIcon ? navIcon : null}
            <span className="mdc-top-app-bar__title">
              {title}
            </span>
          </section>
          {this.renderActionItems()}
        </div>
      </header>
    );
  }

Hay dos elementos de sección en este código HTML. El primero contiene un ícono de navegación y un título. El segundo contiene íconos de acción.

Luego, agrega el método renderActionItems:

renderActionItems() {
  const {actionItems} = this.props;
  if (!actionItems) {
    return;
  }

  return (
    <section className='mdc-top-app-bar__section mdc-top-app-bar__section--align-end' role='toolbar'>
      {/* need to clone element to set key */}
      {actionItems.map((item, key) => React.cloneElement(item, {key}))}
    </section>
  );
}

Un desarrollador importará TopAppBar a su aplicación de React y pasará íconos de acción al elemento TopAppBar. Puedes ver el código de ejemplo que inicializa un TopAppBar en App.js.

Falta el método getMergedStyles, que se usa en el método render. Agrega el siguiente método de JavaScript a la clase TopAppBar:

getMergedStyles = () => {
  const {style} = this.props;
  const {style: internalStyle} = this.state;
  return Object.assign({}, internalStyle, style);
}

this.classes también falta en el método render, pero se analizará en una sección posterior. Además del método get faltante, this.classes, aún hay partes de TopAppBar que debes implementar para que la barra superior de la app se renderice correctamente.

Las partes del componente de React que aún faltan en la barra superior de la app son las siguientes:

  • Una base inicializada
  • Métodos de adaptador para pasar a la base
  • Lenguaje de marcado JSX
  • Administración de variantes (fija, corta, siempre contraída, destacada)

El enfoque

  1. Implementa los métodos Adapter.
  2. Inicializa Foundation en componentDidMount.
  3. Llama al método Foundation.destroy en componentWillUnmount.
  4. Establece la administración de variantes a través de un método get que combina nombres de clases apropiados.

4. Implementa métodos de adaptador

El componente de JS TopAppBar que no es un framework implementa los siguientes métodos de Adapter (que se indican en detalle aquí):

  • hasClass()
  • addClass()
  • removeClass()
  • registerNavigationIconInteractionHandler()
  • deregisterNavigationIconInteractionHandler()
  • notifyNavigationIconClicked()
  • setStyle()
  • getTopAppBarHeight()
  • registerScrollHandler()
  • deregisterScrollHandler()
  • registerResizeHandler()
  • deregisterResizeHandler()
  • getViewportScrollY()
  • getTotalActionItems()

Debido a que React tiene eventos sintéticos y diferentes prácticas recomendadas y patrones de codificación, los métodos Adapter deben volver a implementarse.

Método get del adaptador

En el archivo TopAppBar.js, agrega el siguiente método de JavaScript a TopAppBar:

get adapter() {
  const {actionItems} = this.props;

  return {
    hasClass: (className) => this.classes.split(' ').includes(className),
    addClass: (className) => this.setState({classList: this.state.classList.add(className)}),
    removeClass: (className) => {
      const {classList} = this.state;
      classList.delete(className);
      this.setState({classList});
    },
    setStyle: this.setStyle,
    getTopAppBarHeight: () => this.topAppBarElement.current.clientHeight,
    registerScrollHandler: (handler) => window.addEventListener('scroll', handler),
    deregisterScrollHandler: (handler) => window.removeEventListener('scroll', handler),
    registerResizeHandler: (handler) => window.addEventListener('resize', handler),
    deregisterResizeHandler: (handler) => window.removeEventListener('resize', handler),
    getViewportScrollY: () => window.pageYOffset,
    getTotalActionItems: () => actionItems && actionItems.length,
  };
}

Las APIs del adaptador para el registro de eventos de desplazamiento y cambio de tamaño se implementan de la misma manera que la versión de JS sin framework, ya que React no tiene ningún evento sintético para desplazarse o cambiar de tamaño, y aplaza el sistema de eventos de DOM nativo. getViewPortScrollY también debe diferir al DOM nativo, ya que es una función en el objeto window, que no está en la API de React. Las implementaciones del adaptador serán diferentes para cada framework.

Es posible que notes que falta this.setStyle, al que llama el método get adapter. En el archivo TopAppBar.js, agrega el método de JavaScript faltante a la clase TopAppBar:

setStyle = (varName, value) => {
  const updatedStyle = Object.assign({}, this.state.style);
  updatedStyle[varName] = value;
  this.setState({style: updatedStyle});
}

Acabas de implementar Adapter. Ten en cuenta que es posible que veas errores en la consola en este momento porque la implementación completa aún no está completa. En la siguiente sección, se te guiará para agregar y quitar clases de CSS.

5. Implementa métodos de componentes

Administra variantes y clases

React no tiene una API para administrar clases. Para imitar los métodos de agregar o quitar clases de CSS de JavaScript nativo, agrega la variable de estado classList. Hay tres fragmentos de código en TopAppBar que interactúan con las clases de CSS:

  1. componente <TopAppBar /> a través de la propiedad className
  2. El método Adapter a través de addClass o removeClass
  3. Está codificado de forma fija en el componente React <TopAppBar />.

Primero, agrega la siguiente importación en la parte superior de TopAppBar.js, debajo de las importaciones existentes:

import classnames from 'classnames';

Luego, agrega el siguiente código dentro de la declaración de clase del componente TopAppBar:

export default class TopAppBar extends React.Component {
  constructor(props) {
    super(props);
    this.topAppBarElement = React.createRef();
  }

  state = {
    classList: new Set(),
    style: {},
  };

  get classes() {
    const {classList} = this.state;
    const {
      alwaysCollapsed,
      className,
      short,
      fixed,
      prominent,
    } = this.props;

    return classnames('mdc-top-app-bar', Array.from(classList), className, {
      'mdc-top-app-bar--fixed': fixed,
      'mdc-top-app-bar--short': short,
      'mdc-top-app-bar--short-collapsed': alwaysCollapsed,
      'mdc-top-app-bar--prominent': prominent,
    });
  }

  ... 
}

Si vas a http://localhost:8080, las casillas de verificación de Control ahora deberían activar o desactivar los nombres de clase del DOM.

Este código permite que muchos desarrolladores puedan usar TopAppBar. Los desarrolladores pueden interactuar con la API de TopAppBar sin preocuparse por los detalles de implementación de las clases de CSS.

Ya implementaste correctamente el adaptador. En la siguiente sección, se te guiará para crear una instancia de Foundation.

Activa y desactiva el componente

La creación de instancias Foundation ocurre en el método componentDidMount.

Primero, importa las bases de la barra superior de la app de MDC. Para ello, agrega la siguiente importación después de las importaciones existentes en TopAppBar.js:

import {MDCTopAppBarFoundation, MDCFixedTopAppBarFoundation, MDCShortTopAppBarFoundation} from '@material/top-app-bar';

Luego, agrega el siguiente código JavaScript a la clase TopAppBar:

export default class TopAppBar extends React.Component {
 
  ... 

  foundation_ = null;

  componentDidMount() {
    this.initializeFoundation();
  }

  componentWillUnmount() {
    this.foundation_.destroy();
  }

  initializeFoundation = () => {
    if (this.props.short) {
      this.foundation_ = new MDCShortTopAppBarFoundation(this.adapter);
    } else if (this.props.fixed) {
      this.foundation_ = new MDCFixedTopAppBarFoundation(this.adapter);
    } else {
      this.foundation_ = new MDCTopAppBarFoundation(this.adapter);
    }

    this.foundation_.init();
  }
 
  ... 

}

Una buena práctica de codificación de React es definir propTypes y defaultProps. Agrega la siguiente importación después de las importaciones existentes en TopAppBar.js:

import PropTypes from 'prop-types';

Luego, agrega el siguiente código al final de TopAppBar.js (después de la clase Component):

import PropTypes from 'prop-types';

TopAppBar.propTypes = {
  alwaysCollapsed: PropTypes.bool,
  short: PropTypes.bool,
  fixed: PropTypes.bool,
  prominent: PropTypes.bool,
  title: PropTypes.string,
  actionItems: PropTypes.arrayOf(PropTypes.element),
  navIcon: PropTypes.element,
};

TopAppBar.defaultProps = {
  alwaysCollapsed: false,
  short: false,
  fixed: false,
  prominent: false,
  title: '',
  actionItems: null,
  navIcon: null,
};

Ya implementaste con éxito el componente de React de la barra superior de la app. Si navegas a http://localhost:8080, puedes jugar con la página de demostración. La página de demostración funcionará de la misma manera que la página de demostración de MDC Web. La página de demostración debería verse de la siguiente manera:

3d983b98c2092e7a.png

6. Conclusión

En este instructivo, explicamos cómo unir Foundation de MDC Web para usarlo en una aplicación de React. Hay algunas bibliotecas en GitHub y npm que unen los componentes web de MDC como se describe en Integración en frameworks. Te recomendamos que uses la lista que se encuentra aquí. Esta lista también incluye otros frameworks además de React, como Angular y Vue.

En este instructivo, se destaca nuestra decisión de dividir el código web de MDC en 3 partes: Foundation, Adapter y Component. Esta arquitectura permite que los componentes compartan código común mientras trabajan con todos los frameworks. Gracias por probar Material Components React. Consulta nuestra nueva biblioteca MDC React. Esperamos que hayas disfrutado de este codelab.

Pude completar este codelab en una cantidad de tiempo y con un nivel de esfuerzo razonables

Totalmente de acuerdo De acuerdo Neutral En desacuerdo Totalmente en desacuerdo

Me gustaría seguir usando los componentes de Material en el futuro.

Totalmente de acuerdo De acuerdo Neutral En desacuerdo Totalmente en desacuerdo