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:
@@ -75,8 +75,6 @@ describe("UserPublicProfile", () => {
|
||||
expect(dumpsAll).toHaveAttribute("href", "/@ann/dumps");
|
||||
expect(votesAll).toHaveAttribute("href", "/@ann/upvoted");
|
||||
await waitFor(() => expect(document.title).toMatch(/^ann · /));
|
||||
expect(screen.queryByRole("link", { name: "Settings" })).not
|
||||
.toBeInTheDocument();
|
||||
// The lists are read by the user's id, which a rename doesn't change.
|
||||
expect(calls("GET /api/users/:u/dumps")[0].path).toBe(
|
||||
"/api/users/ann/dumps",
|
||||
@@ -275,11 +273,11 @@ describe("UserPublicProfile", () => {
|
||||
expect(within(bob).getByRole("link", { name: /@cy/ })).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test("visitors can follow; a settings link is only on your own profile", async () => {
|
||||
test("visitors can follow; inviting is only on your own profile", async () => {
|
||||
open({ session: makeSession({ id: "bob" }) });
|
||||
expect(await screen.findByRole("button", { name: "Follow ann" }))
|
||||
.toBeInTheDocument();
|
||||
expect(screen.queryByRole("link", { name: "Settings" })).not
|
||||
expect(screen.queryByRole("button", { name: "+ Invite someone" })).not
|
||||
.toBeInTheDocument();
|
||||
});
|
||||
|
||||
@@ -414,29 +412,18 @@ describe("UserPublicProfile", () => {
|
||||
}
|
||||
});
|
||||
|
||||
test("your settings, and the site's tools for an admin, are a link away", async () => {
|
||||
test("an admin's own profile has no manage tab: that's for others'", async () => {
|
||||
open({
|
||||
session: { ...annSession, user: { ...annSession.user, role: "admin" } },
|
||||
user: { ...ann, role: "admin" },
|
||||
});
|
||||
expect(await screen.findByRole("link", { name: "Settings" }))
|
||||
.toHaveAttribute("href", "/settings");
|
||||
expect(screen.getByRole("link", { name: "Manage" })).toHaveAttribute(
|
||||
"href",
|
||||
"/manage",
|
||||
);
|
||||
// Their own profile has no manage tab: that's for others'.
|
||||
expect(await screen.findByRole("button", { name: "+ Invite someone" }))
|
||||
.toBeInTheDocument();
|
||||
expect(
|
||||
within(document.querySelector(".profile-tabs") as HTMLElement)
|
||||
.queryByRole("button", { name: "Manage" }),
|
||||
).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
test("log out", async () => {
|
||||
const { user } = open({ session: annSession });
|
||||
await user.click(await screen.findByRole("button", { name: "Log out" }));
|
||||
expect(localStorage.getItem(AUTH_STORAGE_KEY)).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe("manage", () => {
|
||||
|
||||
Reference in New Issue
Block a user