diff --git a/.csdd/todo.md b/.csdd/todo.md index e1bfc62..54faa6e 100644 --- a/.csdd/todo.md +++ b/.csdd/todo.md @@ -9,6 +9,16 @@ ## Ready to Land +- [ ] T-043 — Corregir el flash de CTAs del Navbar al cargar el Hero (issue #25) + - Owner: Valen + - Agent: Codex + - Scope: `components/sections/Navbar.tsx`, `app/page.tsx`, `e2e/smoke.spec.ts`, `.csdd/todo.md`; no tocar `PrimaryCta` ni el glow del issue #26 + - Target: `master` + - Updated: 2026-08-17 + - Landing: [draft PR #28](https://github.com/ValenFelizia/787-Rumbos/pull/28) hacia `master`; sin merge manual del issue + - Verification: lint OK (1 warning preexistente en `Footer.tsx`), typecheck OK, build OK, smoke E2E 9/9 OK; QA visual desktop/mobile OK + - Note: el estado inicial del home se hace determinista con `isHome`, evitando depender de `usePathname()` durante render/hidratación. Se preservan transición geométrica, `aria-hidden`, `inert`, foco y CTAs mobile. + ## Blocked ## Pending diff --git a/app/page.tsx b/app/page.tsx index 8087919..814ce8f 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -23,7 +23,7 @@ export default function Home() { return (
- + diff --git a/components/sections/Navbar.tsx b/components/sections/Navbar.tsx index c1f26d4..c4fa48b 100644 --- a/components/sections/Navbar.tsx +++ b/components/sections/Navbar.tsx @@ -3,12 +3,12 @@ * components/sections/Navbar.tsx * * Barra sticky. En desktop, los CTAs se ocultan mientras #hero ocupa el viewport - * (T-028 / T-030) para no duplicar el par del Hero; al scrollear fuera reaparecen. + * (T-028 / T-030 / T-043) para no duplicar el par del Hero; al scrollear fuera + * reaparecen. */ import { useState, useEffect, useRef } from "react"; import Image from "next/image"; import Link from "next/link"; -import { usePathname } from "next/navigation"; import { Menu, X } from "lucide-react"; import { PrimaryCta, SecondaryCta } from "@/components/conversion"; import { useModal } from "@/lib/context/ModalContext"; @@ -34,21 +34,26 @@ function isHeroOccupyingViewport(): boolean { return hero.getBoundingClientRect().bottom > NAV_CLEARANCE_PX; } -export function Navbar() { +type NavbarProps = { + /** The home route needs deterministic SSR markup before client hydration. */ + isHome?: boolean; +}; + +export function Navbar({ isHome = false }: NavbarProps) { const { openModal } = useModal(); - const pathname = usePathname(); const [isOpen, setIsOpen] = useState(false); // Home: arrancar ocultos (Hero ya lleva el par). Otras rutas: siempre visibles. - const [showDesktopCtas, setShowDesktopCtas] = useState(pathname !== "/"); + // Keep this route decision server-deterministic to avoid a hydration flash. + const [showDesktopCtas, setShowDesktopCtas] = useState(!isHome); const menuButtonRef = useRef(null); const menuDialogRef = useRef(null); const closeMenuButtonRef = useRef(null); const desktopCtasRef = useRef(null); const logoLinkRef = useRef(null); - // T-028: CTAs desktop siguen la geometría del Hero (sync + scroll/resize). + // T-028/T-043: CTAs desktop siguen la geometría del Hero (sync + scroll/resize). useEffect(() => { - if (pathname !== "/") { + if (!isHome) { setShowDesktopCtas(true); return; } @@ -69,7 +74,7 @@ export function Navbar() { window.removeEventListener("scroll", syncCtaVisibility); window.removeEventListener("resize", syncCtaVisibility); }; - }, [pathname]); + }, [isHome]); // Si los CTAs se ocultan con foco dentro, devolverlo al logo. useEffect(() => { @@ -190,6 +195,7 @@ export function Navbar() {
{ ).toBeVisible(); }); }); + +test.describe("navbar en el hero", () => { + test("mantiene los CTAs desktop ocultos desde el HTML inicial y al volver arriba", async ({ page }) => { + const response = await page.goto("/", { waitUntil: "domcontentloaded" }); + + expect(response).not.toBeNull(); + const initialHtml = await response!.text(); + const initialDesktopCtaMarkup = initialHtml.match( + /]*data-testid="desktop-navbar-ctas"[^>]*>/ + )?.[0]; + + expect(initialDesktopCtaMarkup).toContain('aria-hidden="true"'); + expect(initialDesktopCtaMarkup).toContain('inert=""'); + + const nav = page.getByRole("navigation", { name: "Navegación principal" }); + const navCtaGroup = nav.getByTestId("desktop-navbar-ctas"); + const navCtaGrid = nav.locator("div.opacity-0").first(); + const navCta = nav.locator('button[aria-label^="Armar viaje"]').first(); + + await expect(navCtaGroup).toHaveAttribute("aria-hidden", "true"); + await expect(navCtaGroup).toHaveAttribute("inert", ""); + await expect(navCtaGrid).toHaveCSS("opacity", "0"); + + await page.evaluate(() => window.scrollTo(0, document.body.scrollHeight)); + await expect(navCta).toBeVisible(); + await expect(navCtaGroup).toHaveAttribute("aria-hidden", "false"); + + await page.evaluate(() => window.scrollTo(0, 0)); + await expect(navCtaGroup).toHaveAttribute("aria-hidden", "true"); + await expect(navCtaGrid).toHaveCSS("opacity", "0"); + + await page.reload({ waitUntil: "domcontentloaded" }); + await expect(nav.locator("div.opacity-0").first()).toHaveCSS("opacity", "0"); + }); +});