Skip to content

feat(list): adiciona indicatorPosition nos componentes de lista - #1253

Merged
igor-monteiro-almeida merged 5 commits into
masterfrom
feat/MR-553-indicator-position
Jul 27, 2026
Merged

feat(list): adiciona indicatorPosition nos componentes de lista#1253
igor-monteiro-almeida merged 5 commits into
masterfrom
feat/MR-553-indicator-position

Conversation

@igor-monteiro-almeida

@igor-monteiro-almeida igor-monteiro-almeida commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

Contexto

A tela de seleção de benefícios do PagBlu (MR-523) posiciona a tag %{installments}x sem acréscimo acima do título do card. Hoje a posição do indicator é fixa no CSS de cada componente, então a tela sairia com layout montado por fora e o DS deixaria de ser a fonte da verdade.

Subtarefa MR-553 de MR-552. Spec: Ledger.

São dois commits — o segundo é o único visível a cliente

1. feat(list) — o enabler, zero mudança visual

ListAction, ListExpandable, ListReadOnly e ListSelectable ganham:

indicatorPosition?: 'inline' | 'above' | 'below'  // default 'inline'

Um ponto compartilhado, não quatro. Os 4 já renderizam ContentList, e .ods-content-list já é a coluna de conteúdo (display:flex; flex-direction:column; flex:1) — exatamente o container que o design pede. O ContentList ganhou um slot opcional de indicator e o SCSS ganhou __indicator com --above/--below. Em inline nada é passado e o host mantém o wrapper atual, byte a byte.

Os 8px do respiro vão no margin do wrapper, nunca em gap do .ods-content-list: o container não tem gap hoje e o espaçamento entre título, descrição e caption vem do margin-top do caption — um gap ali regrediria os 9 componentes que usam o ContentList.

Dois detalhes que o ListSelectable exigia e são fáceis de passar batido:

  • indicatorPosition entra nas deps do useMemo do internalList, senão a posição fica obsoleta ao trocar a prop em runtime (coberto por teste);
  • a posição é repassada ao ListReadOnly no ramo isSelectableDisabled, senão se perde exatamente no estado desabilitado (coberto por teste).

Também exporta ListSelectableProps no barrel — a doc já instruía esse import e ele não existia.

2. fix(list-expandable)⚠️ mudança visual em produção

.ods-list-expandable__main { gap } de 12px para 8px, acompanhando o Figma (itemSpacing do frame Main de 12 → 8, bound a spacingXxs, nas 18 variantes).

Toda tela que usa o ListExpandable verá o ícone 4px mais perto do texto. Raio medido por busca de código: 4 usos, todos em mfe-charge-management — drawer de uso de crédito da rede, detalhes de saldo, drawer de cobrança da rede e banner de status de portabilidade.

Deixa o DS temporariamente inconsistente: 8px no ListExpandable contra 12px em ListAction, ListReadOnly e ListSettings — espelhando o Figma, onde os outros 7 componentes ainda não foram tocados.

Está isolado num commit próprio de propósito: se a mudança visual não for agora, dropa o commit e o enabler segue.

Verificação

Suíte completa 781 testes, 66 suítes — todos passando
Snapshots 115, todos inalterados — é a prova automática de que inline não mexeu em produção
Testes novos 35, incluindo teste próprio do ContentList (que não tinha nenhum)
Não-regressão do blast radius ListSettings, SettingsListItem e TransactionListExpandable — os 3 consumidores do ContentList fora deste escopo
Clique ListSelectable: clicar no indicator alterna checkbox/radio nas 3 posições (não havia teste de clique nesse componente)
Lint eslint com 0 erros

Cada teste referencia o cenário da spec (CT-1 a CT-14). Os de CT-4 comparam asFragment() entre o default e o inline explícito — se divergirem, a retrocompatibilidade quebrou.

Storybook e doc

Story IndicatorPosition nos 4 componentes com as 3 posições lado a lado (alimenta o Chromatic), e seção nova no .mdx de cada um. No ListAction a doc distingue explicitamente indicatorPosition de position (timeline) e menuPosition (dropdown).

Nota sobre inline

inline significa ao lado do texto, no fim do bloco de conteúdo — a posição que os quatro já produzem hoje. A estrutura interna varia (em ListExpandable/ListReadOnly o indicator vive dentro de __trailing), mas a ordem visual renderizada é texto → tag → controle nos quatro, garantida por .ods-content-list { flex: 1 }. O indicator pertence ao bloco de conteúdo, não ao slot do controle.

🤖 Generated with Claude Code


Open in Devin Review

A tela de seleção de benefícios do PagBlu (MR-523) posiciona a tag
"%{installments}x sem acréscimo" acima do título do card. A posição do
indicator era fixa no CSS de cada componente, então a tela sairia com
layout montado por fora do DS.

ListAction, ListExpandable, ListReadOnly e ListSelectable ganham
indicatorPosition?: 'inline' | 'above' | 'below', default 'inline'.

Implementação num ponto compartilhado: os 4 já renderizam ContentList, e
.ods-content-list já é a coluna de conteúdo (flex column). O ContentList
ganha um slot opcional de indicator e o SCSS ganha __indicator com os
modificadores --above/--below. Em 'inline' nada é passado e o host mantém
o wrapper atual, byte a byte.

Os 8px do respiro vão no margin do wrapper do indicator, nunca em gap do
.ods-content-list: o container não tem gap hoje e o espaçamento entre
título, descrição e caption vem do margin-top do caption — um gap ali
regrediria os 9 componentes que usam o ContentList.

Dois detalhes que o ListSelectable exige:
- indicatorPosition entra nas deps do useMemo do internalList, senão a
  posição fica obsoleta ao trocar a prop em runtime;
- a posição é repassada ao ListReadOnly no ramo isSelectableDisabled,
  senão se perde exatamente no estado desabilitado.

Também exporta ListSelectableProps no barrel: a doc já instruía esse
import e ele não existia.

Testes: 35 casos novos, incluindo teste próprio do ContentList (que não
tinha), não-regressão dos 3 consumidores fora de escopo (ListSettings,
SettingsListItem, TransactionListExpandable) e clique no indicator do
ListSelectable nas 3 posições. Suíte: 781 testes, 115 snapshots, todos
inalterados — a prova de que 'inline' não mexeu em produção.

Ref: MR-553
…-> 8px)

Acompanha a decisão de design de 24/07/2026: no Figma o itemSpacing do
frame Main do Card List Expandable passou de 12 para 8, bound à variável
spacingXxs da coleção Ocean Spacing, nas 18 variantes — inclusive nas 6
que já existiam. O gap do Content também é 8, mesmo token.

MUDANÇA VISUAL EM PRODUÇÃO: toda tela que usa o ListExpandable verá o
ícone 4px mais perto do texto. Raio medido por busca de código: 4 usos,
todos em mfe-charge-management — drawer de uso de crédito da rede,
detalhes de saldo, drawer de cobrança da rede e banner de status de
portabilidade.

Deixa o DS temporariamente inconsistente: 8px no ListExpandable contra
12px em ListAction, ListReadOnly e ListSettings. Isso espelha o estado do
Figma, onde os outros 7 componentes ainda não foram tocados.

Commit separado de propósito: é a única mudança visível a cliente da
entrega, e pode ser dropado sem afetar o enabler.

Ref: MR-553

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Devin Review found 6 potential issues.

View 1 additional finding in Devin Review.

Open in Devin Review

Comment thread packages/ocean-core/src/components/_content-list.scss Outdated
Comment thread packages/ocean-core/src/components/_list-expandable.scss Outdated
Comment thread packages/ocean-react/src/ListAction/ListAction.tsx Outdated
Comment thread packages/ocean-react/src/ListExpandable/ListExpandable.tsx Outdated
Comment thread packages/ocean-react/src/ListReadOnly/ListReadOnly.tsx Outdated
Comment thread packages/ocean-react/src/ListSelectable/ListSelectable.tsx Outdated
@igor-monteiro-almeida igor-monteiro-almeida self-assigned this Jul 27, 2026
Co-authored-by: devin-ai-integration[bot] <158243242+devin-ai-integration[bot]@users.noreply.github.com>
…tories

A story IndicatorPosition renderizava a tag fixa nas três posições, sem
como desligá-la — não dava para ver o card sem indicator lado a lado.

Usa a própria prop `indicator` com options/mapping (o padrão que
ListReadOnly e ListExpandable já usam para indicatorOptions), com a opção
withoutIndicator. Sem arg sintético, então o tipo Story segue amarrado às
props do componente.

Descrições das stories traduzidas para inglês, seguindo o review.
…sition

O SonarCloud reprovou o quality gate com new_duplicated_lines_density de
17,5% contra limite de 3%: as quatro stories IndicatorPosition foram
geradas do mesmo template e ficaram quase idênticas.

Move parameters, argTypes, args, os casos e o estilo do container para
src/_stories/components/indicatorPosition.tsx — pasta já reservada e
excluída do build publicado (tsconfig.build.json), então o helper não vai
para o pacote dos consumidores.

Cada story cai para ~12 linhas, com um map sobre os casos em vez de três
blocos de JSX repetidos. O toggle do indicator (options/mapping com
withoutIndicator) fica definido num lugar só.
@sonarqubecloud

Copy link
Copy Markdown

@igor-monteiro-almeida
igor-monteiro-almeida merged commit e98be1d into master Jul 27, 2026
11 of 12 checks passed
@igor-monteiro-almeida
igor-monteiro-almeida deleted the feat/MR-553-indicator-position branch July 27, 2026 18:57
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