teqo-backup/src/components/campaign/map/MapScaleLegend.tsx
Francisco Ignácio Fontoura Solla 66feecd66b simplify(B13): corrige classe agregada, bolhas em pane proprio e registra debitos
O /simplify da escala relativa do mapa achou um bug semantico real em
computeAggregateTerritorialClass — somava o headroom dos slugs e comparava
contra a mediana de UMA unidade, classificando um grupo grande como expansao
quando ele e marginal. A mediana agora escala por slugs.length, com teste.

As bolhas sairam do centro do bounding box para o centroide real e ganharam
pane proprio (z-index 450), porque o bringToFront do hover as escondia atras
do poligono. Legenda e pintura passaram a compartilhar o mesmo predicado,
a copy dos quantis parou de prometer faixas de igual largura, e o subtitulo
do painel parou de dizer "em 2026" com 2022 selecionado (defeito do B11).

Fonte unica onde havia duas: NO_DATA_FILL, o raio das bolhas e
formatPlacementOrdinal (ex-formatMunicipalityVoteRank, agora client-safe em
electionFormat.ts).

Os quatro debitos maiores que o cleanup viraram o fill-in B13+
(docs/plans/escala-dry-pos-b13.md); a flakiness dos e2e, reproduzida em main
limpo, foi para o ledger com a causa afinada.
2026-07-26 05:12:37 -03:00

61 lines
1.9 KiB
TypeScript

import Link from 'next/link'
import { campaignConceptHref, type CampaignConceptId } from '@/lib/campaignIntelligenceConcepts'
import type { MapScaleClassing } from '@/lib/mapScaleClasses'
type MapScaleLegendProps = {
classing: MapScaleClassing
/** What the swatches measure — "votos de Jorge Solla em 2022". */
metricLabel: string
/** What the colour means plus any caveat (small scope, missing data). */
note: string
/** Ties the note to the scale selector through `aria-describedby`. */
noteId: string
/** Glossary entry for this scale, when it has one (E18). */
conceptID?: CampaignConceptId
}
/**
* Discrete counterpart of `ChoroplethLegend`: a swatch per class with its real
* range, weakest on the left. A gradient bar cannot answer "which band is this
* município in", which is the only question a classed map is asked.
*/
export const MapScaleLegend = ({
classing,
metricLabel,
note,
noteId,
conceptID,
}: MapScaleLegendProps) => (
<div className="flex flex-col gap-1.5">
<ul
className="flex flex-wrap items-center gap-x-3 gap-y-1.5"
aria-label={`Faixas de cor para ${metricLabel}`}
>
{classing.classes.map((entry, index) => (
<li key={index} className="flex items-center gap-1.5">
<span
aria-hidden="true"
className="size-3 shrink-0 rounded-[3px] ring-1 ring-foreground/15"
style={{ backgroundColor: entry.fill }}
/>
<span className="text-xs tabular-nums text-muted-foreground">{entry.label}</span>
</li>
))}
</ul>
<p id={noteId} className="text-xs text-muted-foreground">
{note}
{conceptID ? (
<>
{' '}
<Link
href={campaignConceptHref(conceptID)}
className="font-medium underline underline-offset-2"
>
Saiba mais
</Link>
</>
) : null}
</p>
</div>
)