perf(movimento): vitrine e 3D mais leves, WebGL por software lembrado na sessão e suporte ao WebKit
This commit is contained in:
1 parent
9fd1570426
commit
8903f3c57e
28 files changed
+854
-406
No files matched your search
@@ -1,10 +1,11 @@
|
||||
import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
import { act, fireEvent, render, screen, within } from "@testing-library/react";
|
||||
import { renderToString } from "react-dom/server";
|
||||
import { MOBILE_QUERY } from "@/lib/device";
|
||||
import { getFeaturedProjects } from "@/lib/portfolio-data";
|
||||
import { getShowcaseConfig } from "@/lib/project-showcase";
|
||||
import {
|
||||
getShowcaseConfig,
|
||||
videoFractionAtScroll,
|
||||
} from "@/lib/project-showcase";
|
||||
import { ProjectReel } from "./ProjectReel";
|
||||
import { ShowcaseProvider } from "./ShowcaseProvider";
|
||||
import { CAPTION_OUT_MS, reelOffset, videoProgress } from "./showcase-math";
|
||||
@@ -290,11 +291,18 @@ describe("ProjectReel", () => {
|
||||
const layer = activeLayer();
|
||||
expect(layer.querySelector(".ds-reel-title")).toBeNull();
|
||||
expect(caption()).toHaveTextContent("Lavação 047");
|
||||
const p = videoProgress(0.5);
|
||||
// a rolagem do projeto é repartida por igual entre os capítulos: o meio dela é onde o 2º capítulo começa no vídeo
|
||||
const lavacao = getShowcaseConfig("lavacao-047");
|
||||
if (!lavacao) throw new Error("projeto fora da vitrine");
|
||||
const p = videoFractionAtScroll(lavacao, videoProgress(0.5));
|
||||
const desktop = layer.querySelector(
|
||||
".ds-reel-screen video",
|
||||
) as HTMLVideoElement;
|
||||
expect(desktop.currentTime).toBeCloseTo(p * 30, 1);
|
||||
expect(p).toBeCloseTo(
|
||||
lavacao.chapters[1].desktop / lavacao.durations.desktop,
|
||||
9,
|
||||
);
|
||||
// o celular da moldura só tem o pôster: nenhum vídeo dentro dele
|
||||
expect(layer.querySelector(".ds-phone")).toBeInTheDocument();
|
||||
expect(layer.querySelector(".ds-phone video")).toBeNull();
|
||||
@@ -312,7 +320,7 @@ describe("ProjectReel", () => {
|
||||
expect(lavacao).toBeDefined();
|
||||
expect(
|
||||
caption().querySelector(".ds-reel-chapter-count")?.textContent,
|
||||
).toMatch(/^0\d\/03$/);
|
||||
).toMatch(/^0\d\/02$/);
|
||||
const labels = lavacao?.chapters.map((chapter) => chapter.label) ?? [];
|
||||
expect(labels).toContain(
|
||||
caption().querySelector(".ds-reel-chapter strong")?.textContent,
|
||||
@@ -512,16 +520,16 @@ describe("ProjectReel", () => {
|
||||
expect(list.querySelector("h2, h3")?.tagName).toBe("H2");
|
||||
});
|
||||
|
||||
it("limits each project to three chapters, in the index and in the caption", () => {
|
||||
it("limits each project to two chapters, in the index and in the caption", () => {
|
||||
renderReel();
|
||||
for (const layer of document.querySelectorAll(".ds-reel-layer"))
|
||||
expect(
|
||||
layer.querySelectorAll(".ds-reel-ticks li").length,
|
||||
).toBeLessThanOrEqual(3);
|
||||
).toBeLessThanOrEqual(2);
|
||||
const chapters = screen.getByRole("list", {
|
||||
name: "Trechos de SEV Moda Íntima",
|
||||
});
|
||||
expect(within(chapters).getAllByRole("button")).toHaveLength(3);
|
||||
expect(within(chapters).getAllByRole("button")).toHaveLength(2);
|
||||
});
|
||||
|
||||
it("shows the real domain in the address bar, or just the project name, never github.com", () => {
|
||||
@@ -586,7 +594,9 @@ describe("ProjectReel", () => {
|
||||
it("scrolls to a chapter inside the project when its link is pressed", () => {
|
||||
renderReel();
|
||||
const nav = screen.getByRole("navigation", { name: "Projetos da vitrine" });
|
||||
fireEvent.click(within(nav).getByRole("button", { name: /As favoritas/ }));
|
||||
fireEvent.click(
|
||||
within(nav).getByRole("button", { name: /Coleção completa/ }),
|
||||
);
|
||||
act(() => {
|
||||
jest.advanceTimersByTime(1200);
|
||||
});
|
||||
@@ -595,6 +605,11 @@ describe("ProjectReel", () => {
|
||||
const last = calls[calls.length - 1][0] as { top: number };
|
||||
expect(last.top).toBeGreaterThan(reelOffset(weights, 0, 0) * SCROLLABLE);
|
||||
expect(last.top).toBeLessThan(reelOffset(weights, 1, 0) * SCROLLABLE);
|
||||
// o 2º capítulo começa na metade da rolagem do projeto (que vai de REEL_HOLD a 1 - REEL_HOLD do trecho dele)
|
||||
expect(last.top).toBeCloseTo(
|
||||
reelOffset(weights, 0, 0.5) * SCROLLABLE + 2,
|
||||
0,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -729,10 +744,10 @@ describe("ProjectReel: a troca de projeto não duplica nada na tela", () => {
|
||||
expect(titles()).toEqual(["SEV Moda Íntima"]);
|
||||
expect(
|
||||
caption().querySelector(".ds-reel-chapter-count")?.textContent,
|
||||
).not.toBe("01/03");
|
||||
).not.toBe("01/02");
|
||||
});
|
||||
|
||||
it("never moves the caption text: no inline transform, and the CSS only fades it", () => {
|
||||
it("never moves the caption text: no inline transform, while it leaves and when it comes back with the new content", () => {
|
||||
renderReel();
|
||||
moveTo(1, 60);
|
||||
|
||||
@@ -740,17 +755,7 @@ describe("ProjectReel: a troca de projeto não duplica nada na tela", () => {
|
||||
for (const element of caption().querySelectorAll("*"))
|
||||
expect((element as HTMLElement).style.transform).toBe("");
|
||||
|
||||
const css = ["reel-list.css", "pinned-reel.css"]
|
||||
.map((file) => fs.readFileSync(path.join(__dirname, file), "utf8"))
|
||||
.join("\n");
|
||||
const rule = /\.ds-reel-caption \{[\s\S]*?\n\}/.exec(css)?.[0] ?? "";
|
||||
expect(rule).toContain("opacity");
|
||||
expect(rule).not.toMatch(/transform|translate/);
|
||||
const layer = /\.ds-reel-layer \{[\s\S]*?\n\}/.exec(css)?.[0] ?? "";
|
||||
expect(layer).toContain("opacity");
|
||||
expect(layer).not.toMatch(/transform|translate/);
|
||||
|
||||
// comportamento: a legenda sai (atributo, sem estilo inline) e volta com o conteúdo novo sem nunca se deslocar
|
||||
// a legenda sai (atributo, sem estilo inline) e volta com o conteúdo novo sem nunca se deslocar
|
||||
expect(caption()).toHaveAttribute("data-leaving");
|
||||
act(() => {
|
||||
jest.advanceTimersByTime(CAPTION_OUT_MS + 40);
|
||||
@@ -777,8 +782,8 @@ describe("ProjectReel: teclado do índice (roving tabindex)", () => {
|
||||
it("makes the whole index ONE tab stop (the current project), however many projects and chapters it lists", () => {
|
||||
renderReel();
|
||||
const buttons = within(index()).getAllByRole("button");
|
||||
// 5 projetos e os 3 trechos do projeto em cena: 8 botões, uma parada
|
||||
expect(buttons).toHaveLength(5 + 3);
|
||||
// 5 projetos e os 2 trechos do projeto em cena: 7 botões, uma parada
|
||||
expect(buttons).toHaveLength(5 + 2);
|
||||
expect(stops()).toHaveLength(1);
|
||||
expect(stops()[0]).toHaveAttribute("aria-current", "true");
|
||||
expect(stops()[0]).toHaveAccessibleName("SEV Moda Íntima");
|
||||
|
||||
Reference in new issue
Block a user