perf(movimento): sem tempo morto, vídeos regravados e 3D sem React Three Fiber
Legenda do mergulho até Projetos cobrir a cena; transição de página sem blur e com fundo escuro; foco no conteúdo ao trocar de página; listas com marcador e dois pesos de título; vídeos da SEV e da SC Plus regravados sem quadros brancos; scrub a 12-15 fps (-30%); vitrine com uma parada de Tab e setas, contador no celular e CSS sob demanda; âncoras num módulo só (fragment-nav); FramePulse com setInterval; CSS morto removido.
This commit is contained in:
1 parent
f8545cd442
commit
6ac87824f3
68 files changed
+5735
-3300
No files matched your search
@@ -0,0 +1,166 @@
|
||||
import { act, fireEvent, render, screen } from "@testing-library/react";
|
||||
import type { KeyboardEvent } from "react";
|
||||
import { moveRovingFocus, useRovingTabindex } from "./useRovingTabindex";
|
||||
|
||||
/**
|
||||
* Testes de caminhos-base de moveRovingFocus e useRovingTabindex (complemento de useRovingTabindex.test.tsx):
|
||||
* cada tecla de gesto, cada modificador sozinho, grupo de um item só, o item atual escondido por CSS, um alvo que
|
||||
* não é elemento, o foco em algo do grupo que não é item e a parada que deixa de valer.
|
||||
*/
|
||||
|
||||
const KEYS = ["p:0", "p:1", "p:2"];
|
||||
|
||||
function Group({
|
||||
count = 3,
|
||||
validBelow = 99,
|
||||
}: {
|
||||
count?: number;
|
||||
/** Chaves `p:<n>` com n a partir daqui deixam de valer (o item some do grupo). */
|
||||
validBelow?: number;
|
||||
}) {
|
||||
const { groupProps, itemProps } = useRovingTabindex("p:0", (key) => {
|
||||
const [, index] = key.split(":");
|
||||
return Number(index) < validBelow;
|
||||
});
|
||||
return (
|
||||
<nav aria-label="grupo" {...groupProps}>
|
||||
{KEYS.slice(0, count).map((key) => (
|
||||
<button key={key} type="button" {...itemProps(key)}>
|
||||
{`item ${key.slice(2)}`}
|
||||
</button>
|
||||
))}
|
||||
<a href="#fora">link comum</a>
|
||||
</nav>
|
||||
);
|
||||
}
|
||||
|
||||
const item = (index: number) =>
|
||||
screen.getByRole("button", { name: `item ${index}` });
|
||||
|
||||
describe("moveRovingFocus: um gesto por tecla", () => {
|
||||
it.each([
|
||||
["ArrowDown", 2],
|
||||
["ArrowRight", 2],
|
||||
["ArrowUp", 0],
|
||||
["ArrowLeft", 0],
|
||||
["Home", 0],
|
||||
["End", 2],
|
||||
])(
|
||||
"path: %s from the middle item -> focus on item %i, the default is cancelled",
|
||||
(key, expected) => {
|
||||
render(<Group />);
|
||||
item(1).focus();
|
||||
|
||||
expect(fireEvent.keyDown(item(1), { key })).toBe(false);
|
||||
|
||||
expect(item(expected)).toHaveFocus();
|
||||
},
|
||||
);
|
||||
|
||||
it.each(["altKey", "ctrlKey", "metaKey", "shiftKey"] as const)(
|
||||
"path: ArrowDown with %s alone -> left to the browser (not cancelled, focus stays)",
|
||||
(modifier) => {
|
||||
render(<Group />);
|
||||
item(0).focus();
|
||||
|
||||
expect(
|
||||
fireEvent.keyDown(item(0), { key: "ArrowDown", [modifier]: true }),
|
||||
).toBe(true);
|
||||
|
||||
expect(item(0)).toHaveFocus();
|
||||
},
|
||||
);
|
||||
|
||||
it("path: a group with one item only -> every gesture stays on it (and is still cancelled)", () => {
|
||||
render(<Group count={1} />);
|
||||
item(0).focus();
|
||||
|
||||
for (const key of ["ArrowDown", "ArrowUp", "Home", "End"]) {
|
||||
expect(fireEvent.keyDown(item(0), { key })).toBe(false);
|
||||
expect(item(0)).toHaveFocus();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe("moveRovingFocus: quando o gesto não é do grupo", () => {
|
||||
it("path: the current item is itself hidden by CSS -> it is not among the shown ones, so the key is left alone", () => {
|
||||
const original = HTMLElement.prototype.checkVisibility;
|
||||
HTMLElement.prototype.checkVisibility = function check(this: HTMLElement) {
|
||||
return this.textContent !== "item 1";
|
||||
};
|
||||
try {
|
||||
render(<Group />);
|
||||
item(1).focus();
|
||||
|
||||
expect(fireEvent.keyDown(item(1), { key: "ArrowDown" })).toBe(true);
|
||||
|
||||
expect(item(1)).toHaveFocus();
|
||||
} finally {
|
||||
HTMLElement.prototype.checkVisibility = original;
|
||||
}
|
||||
});
|
||||
|
||||
it("path: the browser has no checkVisibility -> every item counts as shown", () => {
|
||||
const original = HTMLElement.prototype.checkVisibility;
|
||||
Reflect.deleteProperty(HTMLElement.prototype, "checkVisibility");
|
||||
try {
|
||||
render(<Group />);
|
||||
item(0).focus();
|
||||
|
||||
expect(fireEvent.keyDown(item(0), { key: "End" })).toBe(false);
|
||||
|
||||
expect(item(2)).toHaveFocus();
|
||||
} finally {
|
||||
HTMLElement.prototype.checkVisibility = original;
|
||||
}
|
||||
});
|
||||
|
||||
it("path: the key target is not an element (the document) -> nothing happens", () => {
|
||||
const preventDefault = jest.fn();
|
||||
const group = document.createElement("div");
|
||||
|
||||
moveRovingFocus({
|
||||
key: "ArrowDown",
|
||||
altKey: false,
|
||||
ctrlKey: false,
|
||||
metaKey: false,
|
||||
shiftKey: false,
|
||||
target: document,
|
||||
currentTarget: group,
|
||||
preventDefault,
|
||||
} as unknown as KeyboardEvent<HTMLElement>);
|
||||
|
||||
expect(preventDefault).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("path: a key that is not a gesture (a letter) -> left alone", () => {
|
||||
render(<Group />);
|
||||
item(0).focus();
|
||||
|
||||
expect(fireEvent.keyDown(item(0), { key: "a" })).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe("useRovingTabindex: a parada do Tab", () => {
|
||||
it("path: focus on something of the group that is not an item -> the tab stop does not move", () => {
|
||||
render(<Group />);
|
||||
act(() => item(2).focus());
|
||||
expect(item(2)).toHaveAttribute("tabindex", "0");
|
||||
|
||||
act(() => screen.getByRole("link", { name: "link comum" }).focus());
|
||||
|
||||
expect(item(2)).toHaveAttribute("tabindex", "0");
|
||||
expect(item(0)).toHaveAttribute("tabindex", "-1");
|
||||
});
|
||||
|
||||
it("path: the remembered stop stops being valid (that item left the group) -> back to the fallback", () => {
|
||||
const { rerender } = render(<Group />);
|
||||
act(() => item(2).focus());
|
||||
expect(item(2)).toHaveAttribute("tabindex", "0");
|
||||
|
||||
rerender(<Group count={2} validBelow={2} />);
|
||||
|
||||
expect(item(0)).toHaveAttribute("tabindex", "0");
|
||||
expect(item(1)).toHaveAttribute("tabindex", "-1");
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user