mirror of
https://github.com/fsolla/teqo.git
synced 2026-11-13 06:43:06 +00:00
Merge branch 'cursor/4c694ada'
This commit is contained in:
commit
2ee9a4fe23
13 changed files with 145 additions and 18 deletions
|
|
@ -7,6 +7,7 @@ alwaysApply: false
|
|||
|
||||
## Status
|
||||
|
||||
- **B25 entregue em código (2026-07-26):** link do território na lista de `/campanha/municipios` — coluna "Território" e card mobile usam `TerritoryLink` → `/campanha/territorios#ti-<slug>`; helper compartilhado `territoryAnchor.ts`; destino com `id` nas linhas parent de `TerritoryList` via `CampaignTable.rowId`; sem migration/action. Gate: tsc/lint/format/knip/check:cycles; unit+int verdes (1 int flake por timeout em suíte cheia, pré-existente); `pnpm build` não rodou neste worktree (falta `PAYLOAD_SECRET` no ambiente). Plano: [`link-territorio-lista-municipios.md`](../../../docs/plans/link-territorio-lista-municipios.md).
|
||||
- **Fill-in "Limitar mapa à Bahia" entregue em código (2026-07-26):** `BahiaMap` trava pan com `maxBounds: L.latLngBounds(BAHIA_BOUNDS).pad(0.1)` + `maxBoundsViscosity: 1` e zoom-out com `applyBahiaMinZoom` (`getBoundsZoom` + padding 16px alinhado ao `fitBounds`, guarda `Number.isFinite`, recalcula no `ResizeObserver` existente). Único arquivo; props/`MunicipalityMapPanel` intactos. Impeccable B: critique 0 P0/P1; harden/optimize out. Gate: tsc/lint/format/knip (P3 pré-existente)/cycles/test (2 timeouts flaky re-rodados verdes)/build; Aikido 0 findings. Plano: [`limitar-mapa-a-bahia.md`](../../../docs/plans/limitar-mapa-a-bahia.md).
|
||||
- **B38 entregue em código (2026-07-26):** sidebar recolhido em tablet no shell de `/campanha`. `CampaignSidebar` deixou `collapsible="none"` e passou a `collapsible="offcanvas"` (gap `w-0` — listas densas ganham largura no iPad). Layout `(app)` lê cookie `sidebar_state` no SSR para `defaultOpen`; `CampaignSidebarViewportDefault` colapsa **uma vez** em viewport 768–1023 px quando não há cookie (`matchMedia` `min-width: 1024px`); após o primeiro toggle o cookie vence. `SidebarTrigger` no header vermelho mobile (`md:hidden`) inalterado; nova fileira slim `hidden md:flex` no inset com o mesmo label pt-BR. Mobile continua Sheet. Sem migration. Gate: tsc/lint/format/knip/check:cycles/522 unit verdes; int 399/400 (1 timeout pré-existente em `municipalityPageData`); build não rodou neste worktree sem `.env.local`. Plano: [`sidebar-recolhido-tablet.md`](../../../docs/plans/sidebar-recolhido-tablet.md). Soft **B18:** com rail collapsed o submenu de filtros salvos exigirá expandir antes do 2º nível.
|
||||
- **B24 entregue em código (2026-07-26):** auto-save + justificativa editável no popover de Tendência de `/campanha/municipios`. Tirou o botão "Salvar" (anti-goal do princípio 4) e o `trendNote` hidden; `MunicipalityListTrendControl` agora espelha a máquina de `MunicipalityListExpectedVotesControl` — draft/display/committed + AbortController + geração, status ~150 ms / nota 600 ms (timer cancelável), flush no blur e no fechamento do Popover, Badge otimista, `Textarea` "Justificativa" (`maxLength` 2000), `Alert` + live region `sr-only`, tooltip B23 lendo a nota exibida. Endpoint `POST /campanha/municipios/political-trend` (mesmo shell CSRF/JSON do B27: `isSameOriginRequest` + `campaignJsonMutationRoute`) chama `setMunicipalityPoliticalTrend` **sem** `revalidatePath` — faceta/`?trend=` reconciliam na próxima navegação. Lista/page deixam de passar `trendFormAction`; `/editar` mantém o form action multi-campo. Sem migration. Gate verde (tsc/lint/format/knip/check:cycles/479 unit/393 int/build); e2e B24 + prewarm da rota; Aikido 0 findings. Plano: [`autosave-tendencia-lista-municipios.md`](../../../docs/plans/autosave-tendencia-lista-municipios.md).
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
# B25 — Link do território na lista de municípios
|
||||
|
||||
Status: rascunho
|
||||
Atualizado em: 2026-07-25
|
||||
Status: entregue
|
||||
Atualizado em: 2026-07-26
|
||||
Item do roadmap: [docs/roadmap.md](../roadmap.md) (Trilha B — superfícies de coordenação, item B25; entrada da página entregue por B21)
|
||||
Impeccable: B — encaixe na célula "Território" da lista existente (`MunicipalityList`), mais o `id` de âncora nas linhas da tabela do B21
|
||||
Appetite: ~0,25 dia eng; sem migration, sem collection, sem server action, sem loader novo — uma célula vira link, um helper puro e um `id` por linha
|
||||
|
|
@ -31,9 +31,9 @@ Brief compacto:
|
|||
|
||||
Na lista `/campanha/municipios` a coluna "Território" é texto morto: em `src/components/campaign/municipality/MunicipalityList.tsx` a definição de coluna `region` tem `cell: (municipality) => municipality.region`, e no card mobile o território aparece dentro de `formatMunicipalityGeographyLabel(...)` como parágrafo. A única coisa que se pode fazer com o território ali é usá-lo como **filtro** (Popover do header, B16 ✓).
|
||||
|
||||
A navegação inversa já existe e é assimétrica: no painel do Início, `TerritoryOverviewTable` (E17 ✓) linka cada TI para `/campanha/municipios?region=<TI>`. Ou seja, do território se chega aos municípios, mas do município não se chega ao território.
|
||||
A navegação inversa já existe e é assimétrica: em `/campanha/territorios` (B21 ✓), cada linha de TI linka para `/campanha/municipios?region=<TI>` (rollup herdado do E17 ✓). Ou seja, do território se chega aos municípios, mas do município não se chega ao território.
|
||||
|
||||
O pedido (2026-07-25) fecha essa assimetria: clicar no território da lista deve levar à página daquele território. Hoje **não existe** superfície de TI fora do painel do Início — a página própria é o **B21** (`/campanha/territorios`, planejado no mesmo dia), que entrega os 27 TIs no sistema de listas e **exclui explicitamente** uma rota de detalhe por TI ("o drill continua sendo a lista de municípios filtrada"). Este item é, portanto, a entrada de navegação para a página do B21 — não uma tela nova.
|
||||
O pedido (2026-07-25) fecha essa assimetria: clicar no território da lista deve levar à página daquele território. A superfície de comparação dos 27 TIs é o **B21** (`/campanha/territorios`), que **exclui explicitamente** uma rota de detalhe por TI ("o drill continua sendo a lista de municípios filtrada"). Este item é, portanto, a entrada de navegação para a página do B21 — não uma tela nova.
|
||||
|
||||
## Objetivos
|
||||
|
||||
|
|
@ -76,7 +76,8 @@ Componentes:
|
|||
- **`src/lib/territoryAnchor.ts`** (novo, puro/client-safe): `territoryAnchorId(region: BahiaIdentityTerritory): string` (`ti-${slugify(region)}`) e `buildTerritoryPageHref(region)` (`/campanha/territorios#${territoryAnchorId(region)}`). Reusa `slugify` de `src/lib/slug.ts`. Unit test curto garantindo estabilidade e unicidade sobre `bahiaIdentityTerritories` (27 âncoras distintas) — é o pino que impede link e `id` de divergirem.
|
||||
- **`TerritoryLink`** (novo, em `src/components/campaign/municipality/`): `<Link>` com o nome do TI, `min-h-11`, estilo alinhado ao link do nome do município (`underline-offset-4 hover:underline`), porém em `text-muted-foreground` para manter a hierarquia atual da coluna. **Depth check:** é um wrapper fino de `Link` **com** duas responsabilidades reais (href canônico + rótulo acessível) e 2–3 call sites imediatos (célula desktop, card mobile, futuro cabeçalho do detalhe); se ficar em 1 call site na implementação, inline o `Link` e mantenha só o helper de href.
|
||||
- **`src/components/campaign/municipality/MunicipalityList.tsx`** (alterado): coluna `region` passa a renderizar `<TerritoryLink region={municipality.region} />`; no card mobile, o parágrafo de geografia usa o link no trecho do território, preservando o sufixo `· ZE N` das zonas (hoje montado por `formatMunicipalityGeographyLabel` em `src/utilities/municipalityLabels.ts` — dividir a formatação em rótulo + sufixo em vez de duplicar a regra).
|
||||
- **Página/tabela do B21** (alterada): cada `<TableRow>` de território recebe `id={territoryAnchorId(row.region)}` e as classes de chegada (`scroll-mt-*` para folgar o header sticky + `target:bg-muted/50`), no precedente de `src/app/(campaign)/campanha/(app)/conceitos/page.tsx`.
|
||||
- **`CampaignTable`** (alterado): prop opcional `rowId` para expor `id` no `<TableRow>` sem duplicar tabelas.
|
||||
- **`TerritoryList`** (B21, alterado): `rowId` nas linhas `parent` com `territoryAnchorId(row.region)` e classes de chegada (`scroll-mt-*` + `target:bg-muted/50`), no precedente de `src/app/(campaign)/campanha/(app)/conceitos/page.tsx`.
|
||||
- **Sem migration, sem collection, sem `Consent`, sem server action, sem alteração de loader.**
|
||||
|
||||
## Dependências
|
||||
|
|
@ -110,8 +111,19 @@ Componentes:
|
|||
- `docs/roadmap.md` (Trilha B / "Demais itens abertos", B25; Janela 1–2; cortes seguros)
|
||||
- [pagina-territorios-identidade.md](pagina-territorios-identidade.md) (B21 — o destino, incl. o Não escopo da rota de detalhe) · [tabela-ti-inicio.md](tabela-ti-inicio.md) (E17 ✓ — rollup e o link inverso) · [camada-territorios-identidade.md](camada-territorios-identidade.md) (E12) · [tooltip-celulas-listas.md](tooltip-celulas-listas.md) (B23 — por que o hover de dado não é deste item)
|
||||
- `src/components/campaign/municipality/MunicipalityList.tsx` — coluna `region` e card mobile a alterar
|
||||
- `src/components/campaign/municipality/TerritoryOverviewTable.tsx` — linhas que recebem o `id` de âncora (e o link inverso já existente)
|
||||
- `src/components/campaign/municipality/TerritoryList.tsx` — linhas parent que recebem o `id` de âncora (link inverso para municípios em `TerritoryListColumns.tsx`)
|
||||
- `src/utilities/municipalityLabels.ts` (`formatMunicipalityGeographyLabel`), `src/lib/slug.ts`, `src/lib/bahiaTerritories.ts` — o que o helper reusa
|
||||
- `src/app/(campaign)/campanha/(app)/conceitos/page.tsx` — precedente de âncora com `scroll-mt` + `target:`
|
||||
- AGENTS.md — Campaign auth e acesso por papel (a página destino é staff-only; `leader` nunca vê a coluna), naming (identificadores em inglês, copy pt-BR), sem migration neste item
|
||||
- `PRODUCT.md` / `DESIGN.md` — Field Desk, anti-goal de dashboard/spreadsheet, hierarquia de links na lista
|
||||
|
||||
## Revisão (2026-07-26)
|
||||
|
||||
Auditoria pré-implementação: o painel `TerritoryOverviewTable` saiu do Início com o B21 — âncoras e link inverso vivem em `TerritoryList` + seam `rowId` em `CampaignTable`.
|
||||
|
||||
## As built (2026-07-26)
|
||||
|
||||
- `src/lib/territoryAnchor.ts` — `territoryAnchorId` / `buildTerritoryPageHref`; unit test com 27 âncoras distintas.
|
||||
- `TerritoryLink` — coluna desktop e trecho de território no card mobile (`municipalityGeographyParts` preserva `· ZE N` fora do link).
|
||||
- `CampaignTable.rowId` — linhas parent em `TerritoryList` com `id` + `scroll-mt-6 target:bg-muted/50`.
|
||||
- Escopo A (só lista); detalhe/dossiê permanecem adiados.
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -13,6 +13,7 @@ import { MunicipalityListGoalCoverageCell } from '@/components/campaign/municipa
|
|||
import { MunicipalityListSignalControl } from '@/components/campaign/municipality/MunicipalityListSignalControl'
|
||||
import { MunicipalityListTrendControl } from '@/components/campaign/municipality/MunicipalityListTrendControl'
|
||||
import { MunicipalitySortableHead } from '@/components/campaign/municipality/MunicipalitySortableHead'
|
||||
import { TerritoryLink } from '@/components/campaign/municipality/TerritoryLink'
|
||||
import { CampaignTransitionAnchor } from '@/components/campaign/shared/CampaignListPending'
|
||||
import { CampaignTable, type CampaignTableColumn } from '@/components/campaign/shared/CampaignTable'
|
||||
import { Badge } from '@/components/ui/Badge'
|
||||
|
|
@ -33,9 +34,9 @@ import {
|
|||
import { cn } from '@/lib/utils'
|
||||
import type { CampaignFormActionState } from '@/utilities/campaignFormActionError'
|
||||
import {
|
||||
formatMunicipalityGeographyLabel,
|
||||
formatTerritorialClassWhy,
|
||||
municipalityColumnDescriptions,
|
||||
municipalityGeographyParts,
|
||||
municipalityKindLabels,
|
||||
municipalityPriorityLabels,
|
||||
territorialClassBadgeVariant,
|
||||
|
|
@ -294,7 +295,7 @@ const municipalityListColumns = ({
|
|||
</MunicipalitySortableHead>
|
||||
),
|
||||
cellClassName: 'max-w-56 whitespace-normal text-muted-foreground',
|
||||
cell: (municipality) => municipality.region,
|
||||
cell: (municipality) => <TerritoryLink region={municipality.region} />,
|
||||
},
|
||||
{
|
||||
id: 'kind',
|
||||
|
|
@ -515,13 +516,15 @@ export const MunicipalityList = (props: MunicipalityListProps) => {
|
|||
const names = advisorNames(municipality, advisorNamesById)
|
||||
const position = municipality.votePosition2022
|
||||
const isPriority = municipality.priority === 'alta'
|
||||
const { region, zoneSuffix } = municipalityGeographyParts(municipality)
|
||||
return (
|
||||
<article key={municipality.id} className="flex flex-col gap-3 rounded-xl border p-4">
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<div className="flex flex-col gap-1">
|
||||
<h3 className="font-medium">{municipality.name}</h3>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
{formatMunicipalityGeographyLabel(municipality)}
|
||||
<TerritoryLink region={region} />
|
||||
{zoneSuffix ? ` ${zoneSuffix}` : null}
|
||||
</p>
|
||||
{position ? <VotePositionReadout position={position} layout="card" /> : null}
|
||||
</div>
|
||||
|
|
|
|||
12
src/components/campaign/municipality/TerritoryLink.tsx
Normal file
12
src/components/campaign/municipality/TerritoryLink.tsx
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
import Link from 'next/link'
|
||||
|
||||
import { buildTerritoryPageHref } from '@/lib/territoryAnchor'
|
||||
|
||||
export const TerritoryLink = ({ region }: { region: string }) => (
|
||||
<Link
|
||||
href={buildTerritoryPageHref(region)}
|
||||
className="inline-flex min-h-11 items-center text-muted-foreground underline-offset-4 hover:underline"
|
||||
>
|
||||
{region}
|
||||
</Link>
|
||||
)
|
||||
|
|
@ -12,6 +12,7 @@ import {
|
|||
EmptyMedia,
|
||||
EmptyTitle,
|
||||
} from '@/components/ui/Empty'
|
||||
import { territoryAnchorId } from '@/lib/territoryAnchor'
|
||||
import { cn } from '@/lib/utils'
|
||||
import {
|
||||
clearTerritoryListFilters,
|
||||
|
|
@ -76,7 +77,10 @@ export const TerritoryList = ({
|
|||
rowKey={(row) =>
|
||||
row.variant === 'parent' ? row.region : `${row.parentRegion}-${row.label}`
|
||||
}
|
||||
rowClassName={(row) => (row.variant === 'sub' ? 'bg-muted/30' : undefined)}
|
||||
rowId={(row) => (row.variant === 'parent' ? territoryAnchorId(row.region) : undefined)}
|
||||
rowClassName={(row) =>
|
||||
row.variant === 'sub' ? 'bg-muted/30' : 'scroll-mt-6 target:bg-muted/50'
|
||||
}
|
||||
empty={<TerritoryListEmptyState state={state} />}
|
||||
/>
|
||||
</>
|
||||
|
|
|
|||
|
|
@ -101,6 +101,8 @@ type CampaignTableProps<Row> = {
|
|||
columns: Array<CampaignTableColumn<Row>>
|
||||
rows: readonly Row[]
|
||||
rowKey: (row: Row) => string | number
|
||||
/** Optional DOM id per row (e.g. hash targets on `/campanha/territorios`). */
|
||||
rowId?: (row: Row) => string | undefined
|
||||
/** Rendered inside a full-width row when there are no rows — the header chrome stays mounted. */
|
||||
empty?: ReactNode
|
||||
/** Screen-reader table description. */
|
||||
|
|
@ -118,6 +120,7 @@ export const CampaignTable = <Row,>({
|
|||
columns,
|
||||
rows,
|
||||
rowKey,
|
||||
rowId,
|
||||
empty,
|
||||
caption,
|
||||
className,
|
||||
|
|
@ -146,6 +149,7 @@ export const CampaignTable = <Row,>({
|
|||
{rows.map((row) => (
|
||||
<TableRow
|
||||
key={rowKey(row)}
|
||||
id={rowId?.(row)}
|
||||
className={typeof rowClassName === 'function' ? rowClassName(row) : rowClassName}
|
||||
>
|
||||
{columns.map((column) => {
|
||||
|
|
|
|||
11
src/lib/territoryAnchor.ts
Normal file
11
src/lib/territoryAnchor.ts
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
import type { BahiaIdentityTerritory } from '@/lib/bahiaTerritories'
|
||||
import { slugify } from '@/lib/slug'
|
||||
|
||||
const TERRITORY_PAGE_PATH = '/campanha/territorios'
|
||||
|
||||
/** Fragment id for parent TI rows — must match `TerritoryList` `rowId` and `TerritoryLink` hrefs. */
|
||||
export const territoryAnchorId = (region: BahiaIdentityTerritory | string): string =>
|
||||
`ti-${slugify(region)}`
|
||||
|
||||
export const buildTerritoryPageHref = (region: BahiaIdentityTerritory | string): string =>
|
||||
`${TERRITORY_PAGE_PATH}#${territoryAnchorId(region)}`
|
||||
|
|
@ -163,15 +163,27 @@ export const getCampaignScopeLabel = (
|
|||
return `${municipalityCount} ${municipalityCount === 1 ? 'município' : 'municípios'}`
|
||||
}
|
||||
|
||||
export const municipalityGeographyParts = (municipality: {
|
||||
region: string
|
||||
kind: Municipality['kind']
|
||||
zoneNumber?: number | null
|
||||
}): { region: string; zoneSuffix: string | null } => ({
|
||||
region: municipality.region,
|
||||
zoneSuffix:
|
||||
municipality.kind === 'zona' && municipality.zoneNumber != null
|
||||
? `· ZE ${municipality.zoneNumber}`
|
||||
: null,
|
||||
})
|
||||
|
||||
/** Short human description of a municipality's geography, e.g. "Chapada Diamantina · ZE 105". */
|
||||
export const formatMunicipalityGeographyLabel = (municipality: {
|
||||
region: string
|
||||
kind: Municipality['kind']
|
||||
zoneNumber?: number | null
|
||||
}): string =>
|
||||
municipality.kind === 'zona' && municipality.zoneNumber != null
|
||||
? `${municipality.region} · ZE ${municipality.zoneNumber}`
|
||||
: municipality.region
|
||||
}): string => {
|
||||
const { region, zoneSuffix } = municipalityGeographyParts(municipality)
|
||||
return zoneSuffix ? `${region} ${zoneSuffix}` : region
|
||||
}
|
||||
|
||||
/**
|
||||
* The 11 real column ids in `/campanha/municipios` — 10 staff columns plus
|
||||
|
|
|
|||
|
|
@ -39,6 +39,31 @@ test.describe('Territórios de Identidade', () => {
|
|||
await expect(page).toHaveURL(/\/campanha\/municipios\?region=Irec%C3%AA/)
|
||||
})
|
||||
|
||||
test('parent territory rows expose hash anchor ids for deep links', async ({
|
||||
campaign,
|
||||
page,
|
||||
}) => {
|
||||
const { fixtures } = campaign
|
||||
const password = fixtures.value('senha')
|
||||
const email = `${fixtures.value('territorios-anchors')}@example.com`
|
||||
await campaign.payload.create({
|
||||
collection: 'campaignUser',
|
||||
data: {
|
||||
name: fixtures.value('Coordenadora Âncoras'),
|
||||
email,
|
||||
password,
|
||||
role: 'coordinator',
|
||||
},
|
||||
depth: 0,
|
||||
})
|
||||
|
||||
await campaign.login(page, email, password)
|
||||
await page.goto('/campanha/territorios')
|
||||
|
||||
await expect(page.locator('#ti-irece')).toBeVisible()
|
||||
await expect(page.locator('#ti-velho-chico')).toBeVisible()
|
||||
})
|
||||
|
||||
test('leader cannot open the territories page', async ({ campaign, page }) => {
|
||||
const { fixtures } = campaign
|
||||
const password = fixtures.value('senha')
|
||||
|
|
|
|||
|
|
@ -233,6 +233,7 @@ describe('campaign visual foundation', () => {
|
|||
|
||||
expect(html).toContain('Seabra')
|
||||
expect(html).toContain('Chapada Diamantina')
|
||||
expect(html).toContain('href="/campanha/territorios#ti-chapada-diamantina"')
|
||||
expect(html).toContain('href="/campanha/municipios/seabra"')
|
||||
expect(html).toContain('1.500')
|
||||
expect(html).toContain('Cenários de estimativa')
|
||||
|
|
|
|||
25
tests/unit/municipalityGeographyParts.unit.spec.ts
Normal file
25
tests/unit/municipalityGeographyParts.unit.spec.ts
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
import { describe, expect, it } from 'vitest'
|
||||
|
||||
import { municipalityGeographyParts } from '@/utilities/municipalityLabels'
|
||||
|
||||
describe('municipalityGeographyParts', () => {
|
||||
it('returns only the region for whole municipalities', () => {
|
||||
expect(
|
||||
municipalityGeographyParts({
|
||||
region: 'Chapada Diamantina',
|
||||
kind: 'municipio',
|
||||
zoneNumber: null,
|
||||
}),
|
||||
).toEqual({ region: 'Chapada Diamantina', zoneSuffix: null })
|
||||
})
|
||||
|
||||
it('keeps the ZE suffix outside the linkable region name', () => {
|
||||
expect(
|
||||
municipalityGeographyParts({
|
||||
region: 'Salvador',
|
||||
kind: 'zona',
|
||||
zoneNumber: 5,
|
||||
}),
|
||||
).toEqual({ region: 'Salvador', zoneSuffix: '· ZE 5' })
|
||||
})
|
||||
})
|
||||
17
tests/unit/territoryAnchor.unit.spec.ts
Normal file
17
tests/unit/territoryAnchor.unit.spec.ts
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
import { describe, expect, it } from 'vitest'
|
||||
|
||||
import { bahiaIdentityTerritories } from '@/lib/bahiaTerritories'
|
||||
import { buildTerritoryPageHref, territoryAnchorId } from '@/lib/territoryAnchor'
|
||||
|
||||
describe('territoryAnchor', () => {
|
||||
it('pins a known territory name to a stable anchor and href', () => {
|
||||
expect(territoryAnchorId('Velho Chico')).toBe('ti-velho-chico')
|
||||
expect(buildTerritoryPageHref('Velho Chico')).toBe('/campanha/territorios#ti-velho-chico')
|
||||
})
|
||||
|
||||
it('produces 27 distinct anchor ids for the official TI catalog', () => {
|
||||
const ids = bahiaIdentityTerritories.map((name) => territoryAnchorId(name))
|
||||
expect(ids).toHaveLength(27)
|
||||
expect(new Set(ids).size).toBe(27)
|
||||
})
|
||||
})
|
||||
Loading…
Reference in a new issue