v4: the avatar menu shows at every width and holds Log out, so a profile keeps only Invite; the profile's dump cards lay out again — your own profile had four buttons in a row, Invite someone, Settings, Manage and Log out, Invite taller than the rest, and on wide screens it was the only way to reach /settings or /manage. The avatar menu now leads the header's icon row at every width, after the name and Playlists text links on wide screens, and ends with Log out under a separator, muted until hovered. The profile keeps Invite someone alone. The menu's button outweighs .app-header-nav button and the themes' button rules (Brutalist and GeoCities list it among their flat buttons), so it reads as one of the menu's links. Since b9ff9c4, a card under 472px is laid out by the narrow-card container query, but the profile columns' dense-card rules still placed the thumb, body and vote themselves, more specifically: the thumb lost its row span, so the title fell under it and every card was a tall, mostly empty box. Those rules now only make the card denser (a smaller thumb, a 2-line title, smaller type) and leave placement to the container query. 670 frontend tests pass, and tsc, eslint, deno fmt, doc:lint and i18n:compile --strict are clean; checked in screenshots in Smooth dark at 1400 and 390px, Brutalist light at 390px, GeoCities light and Kanagawa dark at 1400px, in French.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -3,7 +3,8 @@ import { screen } from "@testing-library/react";
|
||||
import { userEvent } from "@testing-library/user-event";
|
||||
import { UserMenu } from "../../../components/layout/UserMenu.tsx";
|
||||
import { makeSession } from "../../fixtures.ts";
|
||||
import { currentPath, renderWithContexts } from "../../render.tsx";
|
||||
import { currentPath, renderApp, renderWithContexts } from "../../render.tsx";
|
||||
import { AUTH_STORAGE_KEY } from "../../../utils/auth/storage.ts";
|
||||
|
||||
describe("UserMenu", () => {
|
||||
test("opens, links to profile and playlists, closes on Escape and outside", async () => {
|
||||
@@ -41,4 +42,26 @@ describe("UserMenu", () => {
|
||||
expect(currentPath()).toBe("/@ann");
|
||||
expect(screen.queryByRole("menu")).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
test("links to /manage for those who hold category:manage", async () => {
|
||||
const user = userEvent.setup();
|
||||
renderWithContexts(
|
||||
<UserMenu user={makeSession({ role: "admin" }).user} />,
|
||||
);
|
||||
await user.click(screen.getByRole("button", { name: "User menu" }));
|
||||
expect(screen.getByRole("menuitem", { name: "Manage" })).toHaveAttribute(
|
||||
"href",
|
||||
"/manage",
|
||||
);
|
||||
});
|
||||
|
||||
test("logs out", async () => {
|
||||
const session = makeSession({ username: "ann" });
|
||||
renderApp(<UserMenu user={session.user} />, { session });
|
||||
const user = userEvent.setup();
|
||||
await user.click(screen.getByRole("button", { name: "User menu" }));
|
||||
await user.click(screen.getByRole("menuitem", { name: "Log out" }));
|
||||
expect(localStorage.getItem(AUTH_STORAGE_KEY)).toBeNull();
|
||||
expect(screen.queryByRole("menu")).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user