♻️ Make the profile photo Update overlay a button

The "Update" overlay on the profile photo was a span with an on-click,
so the keyboard could not reach it. It is now a button: it shows on
keyboard focus as well as on hover.

The button centers its line in the circle, so a 4px bottom padding keeps
the label where the span drew it (0px difference).

New ds visual test "Profile page controls": the page at rest, the
overlay on hover and on focus, Save enabled, and the Change email link
on hover.

AI-assisted-by: claude-opus-5-5
This commit is contained in:
Eva Marco 2026-10-27 09:41:37 +01:00
parent 08228324db
commit 1d2e6acecb
3 changed files with 49 additions and 2 deletions

View File

@ -630,6 +630,42 @@ test.describe("Onboarding questions controls", () => {
});
});
test.describe("Profile page controls", () => {
const shot = { maxDiffPixelRatio: 0, threshold: 0 };
test("Profile page avatar, form and links", async ({ page }) => {
const dashboardPage = new DashboardPage(page);
await dashboardPage.goToDashboard();
await dashboardPage.goToAccount();
const container = page.locator(
"[class*='settings_profile__form-container']",
);
await expect(page.getByText("Change email")).toBeVisible();
await page.mouse.move(10, 700);
await expect(container).toHaveScreenshot(shot);
// The "Update" overlay shows when the pointer is on the avatar.
await page.locator("[class*='image-change-field']").hover();
await expect(container).toHaveScreenshot(shot);
// Save turns enabled once the name changes.
await page.getByLabel("Your name").fill("Renamed user");
await page.mouse.move(10, 700);
await expect(container).toHaveScreenshot(shot);
await page.getByText("Change email").hover();
await expect(container).toHaveScreenshot(shot);
// The "Update" overlay is a button: it shows on keyboard focus too.
await page.mouse.move(10, 700);
await page.getByRole("button", { name: "Update" }).focus();
await page.keyboard.press("Shift+Tab");
await page.keyboard.press("Tab");
await expect(container).toHaveScreenshot(shot);
});
});
test.describe("Templates section controls", () => {
const shot = { maxDiffPixelRatio: 0, threshold: 0 };

View File

@ -119,8 +119,10 @@
[:form {:class (stl/css :avatar-form)}
[:div {:class (stl/css :image-change-field)}
[:span {:class (stl/css :update-overlay)
:on-click on-image-click} (tr "labels.update")]
[:button {:type "button"
:class (stl/css :update-overlay)
:on-click on-image-click}
(tr "labels.update")]
[:img {:src photo}]
(when has-photo?
[:button {:type "button"

View File

@ -277,7 +277,16 @@ form.avatar-form {
line-height: 6;
text-align: center;
background: var(--color-accent-tertiary);
border: none;
// The button centers its 144px line in the 148px circle; the bottom
// padding keeps the label where the old span drew it.
padding: 0 0 $s-4;
z-index: $z-index-modal;
&:focus-visible {
opacity: 0.8;
}
}
.delete-overlay {