fix(navegacao): foco em âncora de outra página, vitrine acessível e 3D mais leve
Foco cai no destino ao chegar de outra página; âncoras ignoram ?tipo=; movimento reduzido salta em todos os cliques; cronômetro da janela ampliada; degradê no topo de Projetos e legendas do mergulho com intervalos testados; vitrine acima da barra de cookies e com 2 paradas por projeto; reel-list.css no global; 3D montado em tarefas curtas, limitado a ~30 qps e estático com WebGL por software; pôsteres da vitrine de 330 para 158 KB; SC Plus regravado; scripts sem funções acima de complexidade 10.
This commit is contained in:
1 parent
9e2d65a926
commit
204ebd5fa5
49 files changed
+2197
-606
No files matched your search
@@ -0,0 +1,110 @@
|
||||
import { act, render } from "@testing-library/react";
|
||||
import type { CSSProperties } from "react";
|
||||
import HeroField, { DIVE_QUERY } from "./HeroField";
|
||||
|
||||
/*
|
||||
* A altura do mergulho do hero vive num lugar só: `--hero-tail` (globals.css), que o CSS usa na altura do hero e na
|
||||
* margem de Projetos, e que o HeroField lê para saber onde o progresso (--hero-p) acaba: quando Projetos começa a
|
||||
* subir sobre o palco, `--hero-tail` telas antes do fim do hero. Estes testes conferem o comportamento (o progresso
|
||||
* segue a variável), não o texto do CSS.
|
||||
*/
|
||||
|
||||
jest.mock("./HeroFieldScene", () => ({
|
||||
__esModule: true,
|
||||
default: () => null,
|
||||
prepareField: () => {},
|
||||
}));
|
||||
|
||||
const win = window as unknown as Record<string, unknown>;
|
||||
const original = {
|
||||
matchMedia: window.matchMedia,
|
||||
webgl2: win.WebGL2RenderingContext,
|
||||
getContext: HTMLCanvasElement.prototype.getContext,
|
||||
innerHeight: Object.getOwnPropertyDescriptor(window, "innerHeight"),
|
||||
};
|
||||
|
||||
const SCREEN = 800;
|
||||
|
||||
beforeEach(() => {
|
||||
window.matchMedia = jest.fn().mockImplementation((query: string) => ({
|
||||
matches: query === DIVE_QUERY,
|
||||
media: query,
|
||||
addEventListener() {},
|
||||
removeEventListener() {},
|
||||
}));
|
||||
Object.defineProperty(navigator, "hardwareConcurrency", {
|
||||
configurable: true,
|
||||
value: 8,
|
||||
});
|
||||
win.WebGL2RenderingContext = function WebGL2RenderingContext() {};
|
||||
HTMLCanvasElement.prototype.getContext = (() => ({
|
||||
getExtension: () => null,
|
||||
})) as unknown as typeof HTMLCanvasElement.prototype.getContext;
|
||||
Object.defineProperty(window, "innerHeight", {
|
||||
configurable: true,
|
||||
value: SCREEN,
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
window.matchMedia = original.matchMedia;
|
||||
win.WebGL2RenderingContext = original.webgl2;
|
||||
HTMLCanvasElement.prototype.getContext = original.getContext;
|
||||
if (original.innerHeight)
|
||||
Object.defineProperty(window, "innerHeight", original.innerHeight);
|
||||
});
|
||||
|
||||
/** O hero com a altura (px) que o CSS lhe dá, rolado `scrolled` px; devolve o progresso do mergulho. */
|
||||
function progressAt({
|
||||
tail,
|
||||
height,
|
||||
scrolled,
|
||||
}: {
|
||||
tail?: number;
|
||||
height: number;
|
||||
scrolled: number;
|
||||
}) {
|
||||
const style =
|
||||
tail === undefined ? undefined : ({ "--hero-tail": tail } as CSSProperties);
|
||||
const { container, unmount } = render(
|
||||
<section className="hero" style={style}>
|
||||
<HeroField />
|
||||
</section>,
|
||||
);
|
||||
const hero = container.querySelector(".hero") as HTMLElement;
|
||||
hero.getBoundingClientRect = () =>
|
||||
({ top: -scrolled, height, bottom: height - scrolled }) as DOMRect;
|
||||
act(() => {
|
||||
window.dispatchEvent(new Event("resize"));
|
||||
});
|
||||
// o rAF do jsdom corre em ~16 ms
|
||||
return new Promise<number>((resolve) => {
|
||||
setTimeout(() => {
|
||||
const progress = Number(hero.style.getPropertyValue("--hero-p"));
|
||||
unmount();
|
||||
resolve(progress);
|
||||
}, 60);
|
||||
});
|
||||
}
|
||||
|
||||
describe("hero dive: --hero-tail is the one source of its height", () => {
|
||||
it("ends the dive where Projects starts to rise: --hero-tail screens before the end of the hero", async () => {
|
||||
// hero de 2800 px com 2 telas de cauda: o mergulho tem 2800 - 2 * 800 = 1200 px
|
||||
expect(
|
||||
await progressAt({ tail: 2, height: 2800, scrolled: 600 }),
|
||||
).toBeCloseTo(0.5);
|
||||
expect(await progressAt({ tail: 2, height: 2800, scrolled: 1200 })).toBe(1);
|
||||
});
|
||||
|
||||
it("follows the variable: the same hero with a shorter tail has a longer dive", async () => {
|
||||
// 2800 - 1,5 * 800 = 1600 px de mergulho
|
||||
expect(
|
||||
await progressAt({ tail: 1.5, height: 2800, scrolled: 800 }),
|
||||
).toBeCloseTo(0.5);
|
||||
});
|
||||
|
||||
it("treats a hero without --hero-tail as stage only (the dive is the rest of the height)", async () => {
|
||||
// 2000 - 1 * 800 = 1200 px de mergulho
|
||||
expect(await progressAt({ height: 2000, scrolled: 600 })).toBeCloseTo(0.5);
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user