feat(showcase): moldura de navegador, um video por vez e video ampliado nos cards

- cards com moldura de navegador (endereco real) e brilho na cor do site no card que toca
- um video por vez: o do card sob o mouse/foco, ou o mais perto do centro da tela; nada baixa
  antes de tocar, e um botao pausa os videos que se mexem sozinhos
- janela ampliada em <dialog> nativo: troca computador/celular mantendo o ponto do site,
  capitulos clicaveis, controles proprios, setas entre projetos e foco devolvido ao fechar
- dados e capitulos de cada projeto em lib/project-showcase.ts (substitui project-videos.ts)

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
This commit is contained in:
Evandro Fonseca JuniorandClaude Sonnet 5.5 committed 2026-10-08 16:36:53 -03:00
1 parent 60dc2f1878
commit a1a1e91135
18 files changed
+3564 -155

No files matched your search

@@ -0,0 +1,86 @@
import { render, screen } from "@testing-library/react";
import { BrowserFrame, displayUrl } from "./BrowserFrame";
describe("displayUrl", () => {
it("shows the site address without the protocol, www or trailing slash", () => {
expect(displayUrl({ demoUrl: "https://www.example.com.br/" })).toEqual({
text: "example.com.br",
secure: true,
});
expect(displayUrl({ demoUrl: "https://app.example.com/painel/" })).toEqual({
text: "app.example.com/painel",
secure: true,
});
});
it("marks plain http as not secure", () => {
expect(displayUrl({ demoUrl: "http://localhost:3000" })).toEqual({
text: "localhost:3000",
secure: false,
});
});
it("falls back to the code address when there is no live demo", () => {
expect(
displayUrl({ demoUrl: null, githubUrl: "https://github.com/user/repo" }),
).toEqual({ text: "github.com/user/repo", secure: true });
});
it("skips an address that does not parse and uses the next one", () => {
expect(
displayUrl({
demoUrl: "isso não é uma url",
githubUrl: "https://github.com/a/b",
}),
).toEqual({ text: "github.com/a/b", secure: true });
});
it("returns null when there is nothing to show", () => {
expect(displayUrl({})).toBeNull();
expect(displayUrl({ demoUrl: "", githubUrl: null })).toBeNull();
});
});
describe("BrowserFrame", () => {
it("draws the three dots, the address and the screen content", () => {
const { container } = render(
<BrowserFrame url={{ text: "site.com", secure: true }}>
<p>conteúdo</p>
</BrowserFrame>,
);
expect(screen.getByText("site.com")).toBeInTheDocument();
expect(screen.getByText("conteúdo")).toBeInTheDocument();
expect(container.querySelectorAll(".ds-bframe-dots i")).toHaveLength(3);
expect(container.querySelector(".ds-bframe-url svg")).not.toBeNull();
});
it("keeps the decorative bar out of the accessibility tree", () => {
const { container } = render(
<BrowserFrame url={{ text: "site.com", secure: true }}>
<p>x</p>
</BrowserFrame>,
);
expect(container.querySelector(".ds-bframe-bar")).toHaveAttribute(
"aria-hidden",
"true",
);
});
it("shows a neutral label and no lock when the address is unknown or not secure", () => {
const { container, rerender } = render(
<BrowserFrame url={null}>
<p>x</p>
</BrowserFrame>,
);
expect(screen.getByText("pré-visualização")).toBeInTheDocument();
expect(container.querySelector(".ds-bframe-url svg")).toBeNull();
rerender(
<BrowserFrame url={{ text: "localhost", secure: false }}>
<p>x</p>
</BrowserFrame>,
);
expect(container.querySelector(".ds-bframe-url svg")).toBeNull();
});
});