Análise de Componentes Lightning Web (LWC) do Salesforce

This page is not available in the language you requested. You have been redirected to the English version of the page.
Link to this page copied to clipboard

Construa uma configuração de componente passo a passo, usando Salesforce Lightning Web Components como exemplo prático

Free Trial
Not for use with personal data

Os modelos de Componentes Lightning Web (LWC) do Salesforce são arquivos HTML construídos a partir de elementos personalizados: componentes base do Salesforce, como <lightning-button> e <lightning-icon>, e os componentes que você mesmo cria no namespace c-, como <c-my-image>. O Axe DevTools Linter verifica os elementos personalizados assim que você informa o que esses elementos renderizam, usando a opção de configuração global-components.

Este artigo constrói uma configuração LWC do zero. O LWC é um bom exemplo prático porque explora todas as partes da sintaxe de mapeamento, incluindo componentes que não rendem nenhum elemento próprio. O método se aplica a qualquer biblioteca de componentes: descreva o que cada componente renderiza, verifique essa descrição com um arquivo de teste e corrija o mapeamento sempre que houver divergências.

Para uma introdução ao mapeamento de componentes personalizados, veja Analisando Componentes Personalizados. Para a referência completa da sintaxe, veja Configurando o Axe DevTools Linter.

Antes de Começar

Adicione um arquivo axe-linter.yml na raiz do seu projeto. Todos os exemplos abaixo vão nesse arquivo. Se você usar o endpoint REST em vez de uma extensão de editor, a mesma configuração vai no objeto config da requisição, como mostrado em Analisando Componentes Personalizados com o Endpoint REST do Axe DevTools Linter.

Um Mapeamento Descreve o Que o Navegador Recebe

Esta é a ideia que torna todas as outras decisões diretas: um mapeamento descreve o elemento que seu componente renderiza, não a tag que você escreve no modelo.

<lightning-button label="Save"> renderiza um button cujo conteúdo de texto é "Salvar". Escrito como um mapeamento, isso é:

global-components:
  lightning-button:
    element: button
    attributes:
      - label: <text>

O valor especial <text> indica que o atributo label se torna o conteúdo de texto do elemento, o que dá a um botão seu nome acessível. Com esse mapeamento no lugar, o Axe DevTools Linter relata uma violação button-name para <lightning-button> usado sem label, exatamente como faria para um <button> vazio.

Etapa 1: Mapear os Componentes que Rendam um Elemento

Comece com componentes que mapeiem claramente para um único elemento nativo, e mapeie apenas os atributos que contenham informações de acessibilidade.

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: alt

Os nomes dos componentes são sensíveis a maiúsculas e minúsculas e correspondem à tag como você a escreve no modelo, então os nomes em kebab-case do LWC são usados tal como estão.

Para os outros valores especiais (aria-* para passar atributos ARIA, <element> para permitir que um atributo escolha o elemento emitido, e default para declarar um valor que o componente sempre renderiza) veja o passo a passo em Analisando Componentes Personalizados com a Extensão Axe Accessibility Linter para VS Code ou o Plugin JetBrains.

Etapa 2: Declarar os Envolventes que Não Rendam Nada

O LWC usa <template> para um elemento raiz de componente e novamente para iterações e condicionais:

<template>
  <ul>
    <template for:each={items} for:item="item">
      <li key={item.id}>{item.name}</li>
    </template>
  </ul>
</template>

Em HTML, o conteúdo de um elemento <template> é inerte: o navegador não o rende, e a tecnologia assistiva não pode alcançá-lo. O Axe DevTools Linter avalia a marcação que um usuário realmente recebe, então não relata sobre o conteúdo dentro de um <template>. O LWC usa o mesmo nome de tag para uma instrução de tempo de compilação que não produz nenhum elemento, então o linter precisa ser informado sobre no que esses envolventes se transformam. A marcação acima chega ao navegador assim:

<ul>
  <li></li>
  <li></li>
</ul>

Declarar template na sua configuração é o que permite ao linter ver através desses envolventes até a marcação interna. Como um mapeamento precisa nomear algum elemento, e um mapeamento se aplica em todo lugar que a tag aparece, o elemento que você escolhe importa. Trabalhar com duas escolhas com a lista acima mostra por que.

Tentando um Contêiner Genérico

Um div é a primeira suposição natural, já que um envolvente soa como um contêiner genérico:

global-components:
  template: div

O Axe DevTools Linter agora avalia a lista como se você tivesse escrito o <template> como um div:

<ul>
  <div>
    <li></li>
  </div>
</ul>

E relata uma violação na linha <ul>:

list: <ul> and <ol> must only directly contain <li>, <script> or <template> elements

O relatório está correto sobre a marcação que foi dada, mas essa marcação não é o que o navegador recebe. O LWC remove o envolvente, então a lista renderizada contém itens de lista diretamente e é perfeitamente válida. A violação vem do elemento substituto, não do seu modelo.

Correspondendo à Relação Renderizada

Agora mapeie template para o elemento que o navegador realmente encontra dentro da lista:

global-components:
  template: li

O linter avalia a mesma lista assim:

<ul>
  <li>
    <li></li>
  </li>
</ul>

Nenhuma violação list é relatada, porque o <ul> contém diretamente um li, que é um dos elementos que a mensagem de regra acima permite, e é também a relação que o navegador obtém. O elemento li duplicado existe apenas na visão que o linter tem do seu arquivo: ele representa um envolvente que não rende nada, e você nunca o escreve em um modelo.

note

Deixar template fora da sua configuração também evita a violação, mas assim nada dentro dos seus modelos é avaliado, o que é o problema que a Etapa 2 pretendia resolver.

Ambas as Escolhas Verificam o Conteúdo Interno

O elemento substituto afeta apenas como o envolvente em si aparece para o linter. A marcação interna é avaliada de qualquer maneira. Adicione uma imagem sem texto alternativo à mesma 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 e template: li relatam a falta de texto alternativo na imagem:

image-alt: Images must have alternative text

Com template: div você obtém esse resultado mais a violação espúria list, que é a diferença prática entre os dois. Como iterar itens de lista é o uso mais comum de <template>, mapeá-lo para li corresponde à saída renderizada mais frequentemente, e é o ponto de partida recomendado.

note

Três regras verificam se um contêiner contém o tipo certo de filho direto: list para <ul> e <ol>, definition-list para <dl>, e summary-name para <details>. Onde um <template> aparece diretamente dentro de um desses quatro contêineres, seu elemento substituto toma seu lugar, então essas três regras podem não relatar o que fariam para o HTML simples equivalente. Nenhuma outra regra é afetada, e o conteúdo dentro do modelo ainda é verificado por completo.

Etapa 3: Verifique Sua Configuração

Não presuma que um mapeamento se comporta da maneira que você pretendia. A verificação mais confiável é escrever a mesma marcação duas vezes, uma com seus componentes e outra com o HTML simples que você espera que eles renderizem, e confirmar que ambos produzem os mesmos resultados.

Crie um pequeno arquivo de teste, scratch.html, em qualquer lugar do projeto que o Axe DevTools Linter verifica:

<template>
  <c-my-image src="cat.jpg"></c-my-image>
  <lightning-button></lightning-button>
</template>

Depois crie seu gêmeo em HTML simples, scratch-expected.html, contendo a marcação que você espera que esses componentes renderizem:

<img src="cat.jpg">
<button></button>

Com os mapeamentos das Etapas 1 e 2 em vigor, ambos os arquivos relatam as mesmas duas violações: image-alt para a imagem sem texto alternativo, e button-name para o botão sem nome acessível. Resultados correspondentes significam que os mapeamentos descrevem seus componentes corretamente. Uma violação em um arquivo mas não no outro aponta para a configuração em vez de para sua marcação.

Este par também mostra por que a verificação vale a pena. Sem o mapeamento template da Etapa 2, scratch.html não relata nada enquanto scratch-expected.html relata ambas as violações, e esse descompasso é o sinal de que algo na configuração está faltando.

Existem três maneiras convenientes de ver os resultados:

Adicionar o nome do componente a cada resultado torna configurações maiores muito mais fáceis de depurar, porque mostra qual mapeamento produziu uma violação. Veja Analisando Violações de Componentes Personalizados.

Decidindo o Que Um Mapeamento Deve Requerer

Alguns componentes renderizam diferentes marcações dependendo de como são usados, e mapeá-los é uma decisão de política em vez de uma tradução simples. <lightning-icon> é um bom exemplo: um ícone sem alternative-text é decorativo, enquanto um ícone com alternative-text transmite significado.

Mapear <lightning-icon> para img significa que o linter solicita que cada ícone declare seu texto alternativo. Um valor explicitamente vazio satisfaz essa solicitação:

<!-- 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>

Essa é uma convenção útil, porque torna visível a intenção de cada ícone no template em vez de ser implícita por omissão. Se preferir não adotá-la, deixe <lightning-icon> fora da sua configuração e o linter não o avaliará.

Quando um componente sempre renderiza o mesmo valor para um atributo, como um role fixo, use default para registrar esse valor. Um default entra em vigor somente quando o valor não está vazio, e um default pode definir um atributo, mas não pode fornecer conteúdo <text>. Veja Atributos Padrão.

important

Mapeie apenas os atributos que transportam informações de acessibilidade e confirme o que cada componente renderiza antes de mapeá-lo. Os exemplos neste artigo seguem a marcação que os componentes base do Salesforce produzem, mas seus próprios componentes, e qualquer componente que você envolver, precisam da mesma verificação.

Aplicando Isso aos Seus Próprios Componentes

As etapas acima se generalizam para qualquer biblioteca de componentes:

  1. Liste os componentes que renderizam um elemento interativo ou significativo, como botões, links, imagens, controles de formulário e cabeçalhos. Eles lhe dão o máximo de valor com o mínimo de configuração.
  2. Para cada um, observe o que ele renderiza e quais atributos carregam informações de acessibilidade, depois mapeie apenas esses atributos.
  3. Declare wrappers que não renderizam nada por conta própria, escolhendo um elemento substituto que preserve o relacionamento que o navegador vê.
  4. Verifique cada mapeamento em relação ao seu equivalente em HTML puro antes de depender dele.
  5. Revise a configuração quando seus componentes mudarem. Um mapeamento reflete apenas como um componente se comportou no dia em que o mapeamento foi escrito.

Se sua biblioteca for uma das bibliotecas que o Axe DevTools Linter já conhece, você pode pular a maior parte deste trabalho. Veja Bibliotecas de Componentes Pré-configuradas.

Veja Também