fix(ui): botones alineados al cuerpo, FAB grande en PC y botón superior de propuesta

- FAB y aviso PWA flanquean la columna central (720px): right/left
  max(16px, calc((100vw - 720px)/2 + 16px)) para acompañar al contenido
  en pantallas anchas sin pegarse al borde de la ventana
- FAB de 64px en PC (54 en móvil); PWA abajo a la izquierda en PC y móvil
- Nuevo BotonProponerArriba (solo PC): píldora "Proponer local" arriba a
  la derecha, alineada al cuerpo, que abre la misma pasarela
This commit is contained in:
2026-08-16 11:50:59 +02:00
parent 003b1e209f
commit 3ce84531d1
8 changed files with 207 additions and 103 deletions
+40 -2
View File
@@ -1,9 +1,25 @@
// @vitest-environment jsdom
import { afterEach, describe, expect, it, vi } from "vitest";
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import { cleanup, fireEvent, render, screen } from "@testing-library/react";
import BotonFlotante from "../../src/components/BotonFlotante.jsx";
afterEach(cleanup);
const matchMediaMock = (esMovil) => (_query) => ({
matches: esMovil,
media: _query,
addEventListener: () => {},
removeEventListener: () => {},
addListener: () => {},
removeListener: () => {},
});
beforeEach(() => {
// por defecto PC (botón grande); cada prueba puede sobreescribirlo
vi.stubGlobal("matchMedia", matchMediaMock(false));
});
afterEach(() => {
vi.unstubAllGlobals();
cleanup();
});
describe("BotonFlotante", () => {
it("expone su propósito con aria-label (accesible sin hover)", () => {
@@ -36,4 +52,26 @@ describe("BotonFlotante", () => {
fireEvent.click(screen.getByRole("button"));
expect(onClick).toHaveBeenCalledTimes(1);
});
it("está abajo a la derecha, alineado con el borde derecho del cuerpo central, y en PC es más grande", () => {
render(<BotonFlotante onClick={() => {}} />);
const boton = screen.getByRole("button");
const contenedor = boton.closest("div").parentElement; // button → relativo → fijo
expect(contenedor.style.position).toBe("fixed");
expect(contenedor.style.bottom).toBe("0px");
// en pantallas anchas acompaña a la columna de 720px; mínimo 16px
expect(contenedor.getAttribute("style")).toMatch(/right:\s*max\(16px,\s*[^;]*100vw[^;]*720px[^;]*16px[^;]*\)/);
// en PC el FAB mide 64px (en móvil 54)
expect(boton.style.width).toBe("64px");
expect(boton.style.height).toBe("64px");
expect(boton.style.fontSize).toBe("28px");
});
it("en móvil mantiene el tamaño compacto (54px)", () => {
vi.stubGlobal("matchMedia", matchMediaMock(true));
render(<BotonFlotante onClick={() => {}} />);
const boton = screen.getByRole("button");
expect(boton.style.width).toBe("54px");
expect(boton.style.fontSize).toBe("24px");
});
});