Skip to content

fix: aria-label no lápis de editar em metas, orçamento e investimentos (closes #128) - #144

Draft
Guiroos wants to merge 2 commits into
mainfrom
claude/quirky-johnson-9c7ud8
Draft

fix: aria-label no lápis de editar em metas, orçamento e investimentos (closes #128)#144
Guiroos wants to merge 2 commits into
mainfrom
claude/quirky-johnson-9c7ud8

Conversation

@Guiroos

@Guiroos Guiroos commented Sep 3, 2026

Copy link
Copy Markdown
Owner

O que mudou

Adicionado aria-label ao botão de ícone (<Pencil>) de editar em três componentes:

  • components/investimentos/InvestmentEntryDialog.tsx:187-195aria-label="Editar registro" (ramo existing do ternário de três braços)
  • components/configuracao-mes/BudgetOverrideDialog.tsx:125-133aria-label="Editar orçamento"
  • components/metas/GoalDialog.tsx:156-164aria-label="Editar meta" (ramo else do ternário mode === 'create')

Mudança puramente aditiva, sem alteração visual ou de comportamento.

Por que dessa forma

lucide-react@1.8.0 (pinado em package.json:45) marca o <svg> do ícone como aria-hidden="true" quando ele não recebe filho nem prop de a11y (hasA11yProp). Como o <Button size="icon"> nesses três sites só tem o Pencil como filho, o role=button chega ao leitor de tela sem nome nenhum (medido via CDP na auditoria original, #106). O rótulo foi colocado no <Button> (o controle clicável), não no ícone — consistente com os outros 20 controles do repo que já seguem esse padrão.

Segui a proposta da issue #128 à risca (mesmos três arquivos, mesmos textos de rótulo), sem desvio de abordagem.

Como testei

  • npm run lint && npm run format:check && npm run typecheck && npm test — todos verdes (535/535 testes passando)
  • npm run build — build de produção completo sem erros
  • Agente ds-reviewer rodado nos três diffs isoladamente — aprovado sem violações (mudança é só a prop aria-label, não toca tokens/classes/estrutura)
  • npm run test:integration não foi executado (exige credenciais Neon e só roda em push para main) — não se aplica de qualquer forma, pois a mudança não toca camada de dados

Novo arquivo de teste: __tests__/unit/a11y-nomes-acessiveis.test.ts, criado porque a #126 (que criaria esse arquivo primeiro) ainda não foi mergeada. Cada asserção ancora no ramo/bloco JSX específico do botão-alvo — não em toMatch(/aria-label/) genérico sobre o arquivo — porque os três componentes têm mais de um <Button> no mesmo trecho, e uma asserção solta passaria com qualquer um deles rotulado. Ajustado após review humano (ver thread resolvido): a asserção do BudgetOverrideDialog inicialmente rodava sobre o arquivo inteiro e não pegava rótulo no botão errado — corrigida para ancorar no <Button> mais próximo do ícone Pencil, verificado por mutação.

Risco e o que NÃO foi coberto

Risco baixo: mudança aditiva de um atributo ARIA, sem impacto em lógica, estilo ou dados. Não cobre os outros 6 sites do achado original (#106) — esses já têm issues próprias: #126 (DS, DeleteButton), #127 (lápis em /categorias e /contas), #129 (os dois "X" em SplitSection.tsx).

Colisão de arquivo esperada. __tests__/unit/a11y-nomes-acessiveis.test.ts está sendo criado do zero por três PRs em paralelo — este, #139 (fatia de #126) e #140 (fatia de #127). Quem mergear por último resolve o conflito somando os describe blocks de cada PR, não descartando um lado.

Arquivos tocados

  • components/investimentos/InvestmentEntryDialog.tsx
  • components/configuracao-mes/BudgetOverrideDialog.tsx
  • components/metas/GoalDialog.tsx
  • __tests__/unit/a11y-nomes-acessiveis.test.ts (novo)

🤖 Generated with Claude Code

https://claude.ai/code/session_012WLtBDDRT1KDQqQtNjTgjK

closes #128)

Os três lápis de editar em GoalDialog, BudgetOverrideDialog e
InvestmentEntryDialog envolvem só um ícone Pencil sem aria-label — o
Icon do lucide-react@1.8.0 marca o svg como aria-hidden quando não
recebe filho nem prop de a11y, então o botão chega ao leitor de tela
como role=button name="".

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012WLtBDDRT1KDQqQtNjTgjK

@Guiroos Guiroos left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Revisão do head 14fda0e. A correção está certa e fecha a #128 por inteiro (exigência 8): os 3 sites — InvestmentEntryDialog.tsx:192 (ramo existing), BudgetOverrideDialog.tsx:130, GoalDialog.tsx:161 — estão rotulados, com os textos que a issue nomeou, no <Button> e não no <Pencil>. O {...props} de components/ui/button.tsx:80 repassa o atributo ao <button> real, então o nome chega mesmo à árvore. Nenhuma substituição silenciosa de abordagem na correção.

Rodei os gates nesta sessão, sobre este head: npm ci, npm run lint, npm run format:check, npm run typecheck e npm test verdes (35 arquivos, 534 testes).

Um achado, nit — sobre o alcance do gate, não sobre o fix. Detalhe, reprodução e correção verificada no comentário inline.

Sendo justo com a força dele: as duas correções erradas realistas são barradas, e eu mutei o fonte para checar em vez de deduzir. Fix revertido nos três arquivos → 3 testes vermelhos. aria-label no <Pencil> em vez do <Button> (a errada que a #128 nomeia) → vermelho nos três. Vale a mesma ressalva que ficou registrada na revisão do #139: essa segunda só é barrada porque a asserção exige o atributo sozinho numa linha e o printWidth: 100 do prettier mantém <Pencil className="h-3 w-3" aria-label="Editar orçamento" /> (64 colunas) numa linha só — conferido com prettier --check. A margem é estreita e não está sob gate nenhum.

Duas notas que não são achado:

1. Colisão de arquivo com o #140, não declarada aqui. O corpo diz que __tests__/unit/a11y-nomes-acessiveis.test.ts foi criado "porque a #126 ainda não foi mergeada", mas hoje são três PRs abertos criando este mesmo arquivo do zero: #139 (#126), #140 (#127) e este. O #140 registra a colisão na seção de risco e diz o que fazer (somar os describe, não descartar um lado); vale acrescentar a mesma linha aqui, porque quem mergear por último resolve o conflito sem esse aviso.

2. A lista de consumidores do site 2 na #128 está mais larga que a realidade — o que não muda o PR, só o impacto declarado. O gatilho do InvestmentEntryDialog só renderiza com !isControlled (:184-186). Dos três consumidores que a issue lista, InvestmentTypeCard.tsx:322 e dashboard/InvestmentList.tsx:60 passam open/onOpenChange e chegam pelo RowActions (kebab, já nomeado) — o lápis deste componente não é renderizado ali. Quem de fato recebe o rótulo é InvestmentTypeAccordion.tsx:297, que é uncontrolled. O site tem consumidor (exigência 7 satisfeita) e a correção continua certa; a superfície é uma, não três.


Generated by Claude Code

Comment thread __tests__/unit/a11y-nomes-acessiveis.test.ts Outdated
… ícone

Achado no review de #144: expect(source).toMatch(/aria-label/) sobre o
arquivo inteiro passava mesmo com o rótulo no botão errado ("Usar
padrão"), porque o gatilho não é ramo de ternário e o arquivo tem dois
outros <Button> fora dele. Verificado por mutação: reverter o rótulo
do trigger e colocá-lo no botão de reset deixava o teste anterior
verde; a nova captura (mais próximo do <Pencil>) falha corretamente.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012WLtBDDRT1KDQqQtNjTgjK

Guiroos commented Sep 3, 2026

Copy link
Copy Markdown
Owner Author

Respondendo à revisão de 14fda0e:

Suíte completa após o ajuste: npm run lint && npm run format:check && npm run typecheck && npm test verdes (535/535) e npm run build sem erros.


Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants