Linting de Componentes Lightning Web de Salesforce (LWC)
Construya paso a paso una configuración de componente, utilizando los Lightning Web Components de Salesforce como un ejemplo práctico
Las plantillas de los Componentes Lightning Web de Salesforce (LWC) son archivos HTML construidos a partir de elementos personalizados: componentes base de Salesforce como <lightning-button> y <lightning-icon>, y los componentes que usted mismo crea en el espacio de nombres c-, como <c-my-image>. Axe DevTools Linter verifica los elementos personalizados una vez que le indica qué representan dichos elementos, usando la opción de configuración global-components.
Este artículo construye una configuración LWC desde cero. LWC es un buen ejemplo práctico porque ejercita todas las partes de la sintaxis de mapeo, incluidos los componentes que no renderizan ningún elemento propio. El método se aplica a cualquier biblioteca de componentes: describa lo que renderiza cada componente, verifique esa descripción contra un archivo de prueba, y luego corrija el mapeo donde los dos no coincidan.
Para una introducción al mapeo de componentes personalizados, vea Linting de Componentes Personalizados. Para la referencia completa de sintaxis, vea Configurando Axe DevTools Linter.
Antes de Comenzar
Agregue un archivo axe-linter.yml a la raíz de su proyecto. Cada ejemplo a continuación se coloca en ese archivo. Si usa el endpoint REST en lugar de una extensión de editor, la misma configuración va en el objeto config de la solicitud, tal como se muestra en Linting de Componentes Personalizados con el EndPoint REST de Axe DevTools Linter.
Un Mapeo Describe lo que el Navegador Recibe
Esta es la idea que simplifica cada otra decisión: un mapeo describe el elemento que renderiza su componente, no la etiqueta que escribe en la plantilla.
<lightning-button label="Save"> renderiza un button cuyo contenido de texto es "Guardar". Escrito como un mapeo, eso es:
global-components:
lightning-button:
element: button
attributes:
- label: <text>El valor especial <text> indica que el atributo label se convierte en el contenido de texto del elemento, que es lo que le da a un botón su nombre accesible. Con ese mapeo en su lugar, Axe DevTools Linter reporta una violación button-name para <lightning-button> usado sin label, exactamente como lo haría para un <button> vacío.
Paso 1: Mapear los Componentes que Renderizan un Elemento
Comience con componentes que se mapearán sin problemas a un solo elemento nativo, y solo mapee los atributos que llevan información de accesibilidad.
global-components:
# Salesforce base components
lightning-button:
element: button
attributes:
- label: <text>
lightning-icon:
element: img
attributes:
- alternative-text: alt
# Components you author yourself
c-my-image:
element: img
attributes:
- alternative-text: altLos nombres de los componentes son sensibles a mayúsculas y minúsculas y coinciden con la etiqueta tal como la escribe en la plantilla, por lo que los nombres en kebab-case de LWC se usan tal cual.
Para los otros valores especiales (aria-* para pasar los atributos ARIA, <element> para permitir que un atributo elija el elemento emitido, y default para declarar un valor que el componente siempre renderiza) vea el recorrido en Linting de Componentes Personalizados con la Extensión Axe Accessibility Linter para VS Code o el Plugin JetBrains.
Paso 2: Declarar los Wrappers que no Renderizan Nada
LWC usa <template> para el elemento raíz de un componente y nuevamente para iteraciones y condicionales:
<template>
<ul>
<template for:each={items} for:item="item">
<li key={item.id}>{item.name}</li>
</template>
</ul>
</template>En HTML, el contenido de un elemento <template> es inerte: el navegador no lo renderiza y la tecnología asistiva no puede acceder a él. Axe DevTools Linter evalúa el marcado que un usuario realmente obtiene, por lo que no reporta contenido dentro de un <template>. LWC usa el mismo nombre de etiqueta para una instrucción en tiempo de compilación que no produce ningún elemento en absoluto, por lo que se necesita informar al linter en qué se convierten estos wrappers. El marcado anterior llega al navegador así:
<ul>
<li>…</li>
<li>…</li>
</ul>Declarar template en su configuración es lo que permite al linter ver a través de esos wrappers hasta el marcado interior. Debido a que un mapeo tiene que nombrar algún elemento, y un mapeo se aplica dondequiera que aparezca la etiqueta, el elemento que elija importa. Trabajar a través de dos opciones con la lista anterior muestra por qué.
Intentando un Contenedor Genérico
Un div es la primera suposición natural, ya que un wrapper suena como un contenedor genérico:
global-components:
template: divAxe DevTools Linter ahora evalúa la lista como si hubiera escrito el <template> como un div:
<ul>
<div>
<li>…</li>
</div>
</ul>E informa una violación en la línea <ul>:
list: <ul> and <ol> must only directly contain <li>, <script> or <template> elementsEl informe es correcto sobre el marcado que se le dio, pero ese marcado no es lo que recibe el navegador. LWC elimina el wrapper, por lo que la lista renderizada contiene elementos de lista directamente y es perfectamente válida. La violación proviene del elemento sustituto, no de su plantilla.
Igualando la Relación Renderizada
Ahora mapee template al elemento que el navegador realmente encuentra dentro de la lista:
global-components:
template: liEl linter evalúa la misma lista de esta manera:
<ul>
<li>
<li>…</li>
</li>
</ul>No se reporta una violación de list, porque el <ul> contiene directamente un li, que es uno de los elementos que el mensaje de la regla anterior permite, y es la relación que el navegador también obtiene. El li duplicado solo existe en la vista del linter de su archivo: representa un wrapper que no renderiza nada, y nunca lo escribe en una plantilla.
Dejar template fuera de su configuración también evita la violación, pero entonces nada dentro de sus plantillas es evaluado en absoluto, lo cual es el problema que el Paso 2 pretendía resolver.
Ambas Opciones Verifican el Contenido Interno
El elemento sustituto afecta solo cómo aparece el propio wrapper al linter. El marcado interior se evalúa de cualquier manera. Agregue una imagen sin texto alternativo a la misma lista:
<template>
<ul>
<template for:each={items} for:item="item">
<li key={item.id}><img src={item.url}></li>
</template>
</ul>
</template>Ambos template: div y template: li reportan el texto alternativo faltante en la imagen:
image-alt: Images must have alternative textCon template: div obtiene ese resultado más la violación espuria de list, que es la diferencia práctica entre los dos. Debido a que iterar elementos de lista es el uso más común de <template>, mapearlo a li coincide con el resultado renderizado más a menudo, y es el punto de partida recomendado.
Tres reglas comprueban que un contenedor tenga el tipo correcto de hijo directo: list para <ul> y <ol>, definition-list para <dl>, y summary-name para <details>. Donde un <template> está directamente dentro de uno de esos cuatro contenedores, su elemento sustituto toma su lugar, por lo que estas tres reglas pueden no reportar lo que harían para el HTML plano equivalente. Ninguna otra regla se ve afectada, y el contenido dentro de la plantilla sigue siendo verificado en su totalidad.
Paso 3: Verificar su Configuración
No suponga que un mapeo se comporta de la manera que usted pretendía. La verificación más confiable es escribir el mismo marcado dos veces, una con sus componentes y otra como el HTML plano que espera que esos componentes rendericen, y confirmar que ambos produzcan los mismos resultados.
Cree un pequeño archivo de prueba, scratch.html, en cualquier lugar del proyecto que Axe DevTools Linter verifique:
<template>
<c-my-image src="cat.jpg"></c-my-image>
<lightning-button></lightning-button>
</template>Luego cree su gemelo en HTML plano, scratch-expected.html, que contenga el marcado que espera que esos componentes rendericen:
<img src="cat.jpg">
<button></button>Con los mapeos de los Pasos 1 y 2 en su lugar, ambos archivos reportan las mismas dos violaciones: image-alt para la imagen sin texto alternativo, y button-name para el botón sin nombre accesible. Resultados coincidentes significan que los mapeos describen sus componentes correctamente. Una violación en un archivo pero no en el otro apunta a la configuración en lugar de a su marcado.
Este par también muestra por qué vale la pena realizar la verificación. Sin el mapeo template del Paso 2, scratch.html no reporta nada en absoluto mientras scratch-expected.html reporta ambas violaciones, y esa falta de coincidencia es la señal de que falta algo en la configuración.
Hay tres formas convenientes de ver los resultados:
- En VS Code o un IDE JetBrains, abra el archivo y lea los errores destacados, como se describe en Linting de Componentes Personalizados con la Extensión Axe Accessibility Linter para VS Code o el Plugin JetBrains.
- Con el Conector Axe DevTools Linter, ejecútelo contra ambos archivos en un solo comando.
- Con el endpoint REST, publique el contenido de cada archivo con su configuración en el objeto
config. Vea Linting de Componentes Personalizados con el EndPoint REST de Axe DevTools Linter.
Agregar el nombre del componente a cada resultado hace que las configuraciones más grandes sean mucho más fáciles de depurar, porque muestra qué mapeo produjo una violación. Vea Análisis de Violaciones de Componentes Personalizados.
Decidir Qué Debería Requerir un Mapeo
Algunos componentes renderizan diferente marcado dependiendo de cómo se usan, y mapearlos es una decisión de política en lugar de una simple traducción. <lightning-icon> es un buen ejemplo: un ícono sin alternative-text es decorativo, mientras que un ícono con alternative-text transmite significado.
Mapear <lightning-icon> a img significa que el linter pide a cada ícono declarar su texto alternativo. Un valor explícitamente vacío satisface esa solicitud:
<!-- Reported: an image with no alternative text -->
<lightning-icon icon-name="utility:check"></lightning-icon>
<!-- Not reported: explicitly decorative -->
<lightning-icon icon-name="utility:check" alternative-text=""></lightning-icon>Eso es una convención útil, porque hace que la intención de cada ícono sea visible en la plantilla en lugar de ser implícita por omisión. Si prefiere no adoptarla, deje <lightning-icon> fuera de su configuración y el linter no lo evaluará.
Cuando un componente siempre renderiza el mismo valor para un atributo, como un role fijo, use default para registrar ese valor. Un default surte efecto solo cuando el valor no está vacío, y un default puede establecer un atributo pero no puede proporcionar contenido de <text>. Vea Atributos Predeterminados.
Mapee solo los atributos que llevan información de accesibilidad y confirme lo que cada componente renderiza antes de mapearlo. Los ejemplos en este artículo siguen el marcado que producen los componentes base de Salesforce, pero sus propios componentes, y cualquier componente que usted envuelva, necesitan la misma verificación.
Aplicar Esto a Sus Propios Componentes
Los pasos anteriores se generalizan a cualquier biblioteca de componentes:
- Enumere los componentes que renderizan un elemento interactivo o significativo, como botones, enlaces, imágenes, controles de formularios y encabezados. Eso le brinda el mayor valor por la menor configuración.
- Para cada uno, anote lo que renderiza y qué atributos llevan información de accesibilidad, luego mapee solo esos atributos.
- Declare envoltorios que no rendericen nada propio, eligiendo un elemento sustituto que preserve la relación que el navegador ve.
- Verifique cada mapeo contra su equivalente en HTML plano antes de depender de él.
- Revise la configuración cuando sus componentes cambien. Un mapeo solo refleja cómo se comportaba un componente el día en que se realizó el mapeo.
Si su biblioteca es una de las que Axe DevTools Linter ya conoce, puede omitir la mayor parte de este trabajo. Vea Bibliotecas de Componentes Preconfiguradas.
Consulte También
- Linterización de Componentes Personalizados
- Linterización de Componentes Personalizados con la Extensión de Linter de Accesibilidad Axe para VS Code o el Plugin de JetBrains
- Linterización de Componentes Personalizados con el Endpoint REST de Axe DevTools Linter
- Configuración del Linter de Axe DevTools
- Bibliotecas de Componentes Preconfiguradas
