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, a la vez que respeta los principios de Material Design. En el siguiente codelab, se explica cómo compilar un componente de React, que usa MDC Web como base. Los principios aprendidos en este codelab se pueden aplicar a cualquier framework de JavaScript.

Cómo se crea MDC Web

La capa de JavaScript de MDC Web está compuesta por 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 con HTML en Adapter. Foundation tiene un Adapter.

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

El componente tiene un elemento Base y su función es

  1. Implementa Adapter con JavaScript que no sea un framework.
  2. Proporciona métodos públicos que usen un proxy a los métodos de Foundation.

Qué proporciona MDC Web

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

Para integrar MDC Web en un framework, debes crear tu propio Componente en el lenguaje o la sintaxis de ese framework. El framework Component implementa Adapter 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 React de Material Design. Abarca 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. El diseño de la página de demostración ya está configurado, por lo que puedes comenzar a trabajar en la barra superior de la app. La barra superior de la aplicación incluirá lo siguiente:

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

Requisitos:

  • Una versión reciente de Node.js (que viene 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 dependencias del proyecto

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

npm install

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

22a33efc2a687408.png

Cómo ejecutar la app de inicio

En el mismo directorio, ejecuta lo siguiente:

npm start

Se iniciará la 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 la barra superior de la app debería estar ejecutándose en tu navegador. Deberías ver una pared de texto lorem ipsum, un cuadro Controls (parte inferior derecha) y una barra superior de la app sin terminar:

4ca3cf6d216f9290.png

Consulta el código y el proyecto

Si abres tu 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 vacía 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 HTML del componente. El componente de la barra superior de la app renderizará una etiqueta <header /> y estará compuesta por 2 secciones principales:

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

Si tienes preguntas sobre los elementos que componen la Barra superior de la app, visita 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 HTML. El primero contiene un ícono de navegación y un título. La segunda contiene íconos de acción.

A continuación, 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 un 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 tampoco está presente en el método render, pero se tratará en una sección posterior. Además del método get que falta, this.classes, todavía hay partes de TopAppBar que debes implementar para que la barra superior de la app pueda renderizarse 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 Adapter 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 adecuados.

4. Cómo implementar métodos de Adapter

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 de adaptador para el registro de eventos de desplazamiento y cambio de tamaño se implementan de manera idéntica a la versión de JS que no es del framework, ya que React no tiene ningún evento sintético para desplazarse o cambiar el tamaño, y difiere al sistema de eventos nativo del DOM. 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 de adaptadores serán diferentes para cada framework.

Tal vez notes que falta this.setStyle, que se llama con 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, en este punto, es posible que veas errores en tu consola debido a que aún no se completó la implementación. En la siguiente sección, se explica cómo agregar y quitar clases de CSS.

5. Cómo implementar métodos de componentes

Administra variantes y clases

React no tiene una API para administrar clases. Para imitar los métodos de clase CSS de agregar o quitar los JavaScript nativos, agrega la variable de estado classList. Hay tres fragmentos de código en TopAppBar que interactúan con 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. Codificado de forma fija dentro del componente <TopAppBar /> de React.

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 te diriges a http://localhost:8080, las casillas de verificación Controles ahora deberían activar o desactivar los nombres de las clases 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, aprenderás a crear una instancia de Foundation.

Cómo montar y desmontar 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 práctica recomendada para la programación en 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 web de MDC. La página de demostración debería verse de la siguiente manera:

3d983b98c2092e7a.png

6. Conclusión

En este instructivo, abordamos 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 utilices 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.

Muy de acuerdo De acuerdo Ni feliz ni triste En desacuerdo Muy en desacuerdo .
.