From d975dfd5a4389c18253094061fc5dd6acfced3a7 Mon Sep 17 00:00:00 2001 From: Devon Hudson Date: Fri, 2 Oct 2026 22:47:30 +0000 Subject: [PATCH] Show the errors of pinned registration fields above the form The registration form doesn't render a username pinned by the invite, and shows a pinned email address as text, so errors the server reports on either field weren't shown at all: the form just came back. The invitee never chose these values, so "This username is already taken" would mean nothing to them either. Each pinned field with an error now gets its own message above the form, saying the invite can't be used and to let whoever sent it know: a policy which refuses the address or the username would refuse a new invite just the same. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01CZdGaj9bJcn5QeeUw8YXoF --- frontend/locales/en.json | 2 + .../components/RegisterForm/RegisterForm.tsx | 9 +++ frontend/src/entrypoints/register.test.tsx | 60 +++++++++++++++++++ 3 files changed, 71 insertions(+) diff --git a/frontend/locales/en.json b/frontend/locales/en.json index c16b77139..da0b553d9 100644 --- a/frontend/locales/en.json +++ b/frontend/locales/en.json @@ -258,6 +258,8 @@ "password_confirm_label": "Confirm password", "password_label": "Password", "passwordless_notice": "This account doesn't need a password. We'll send a verification code to your email address instead.", + "pinned_email_error": "This invite's email address can't be used to create an account. Let whoever invited you know.", + "pinned_username_error": "The account this invite is for can't be created. Let whoever invited you know.", "send_code": "Send code", "terms_of_service": "I agree to the Terms and Conditions", "username_available": "This username is available", diff --git a/frontend/src/components/RegisterForm/RegisterForm.tsx b/frontend/src/components/RegisterForm/RegisterForm.tsx index 321991532..1f58e154b 100644 --- a/frontend/src/components/RegisterForm/RegisterForm.tsx +++ b/frontend/src/components/RegisterForm/RegisterForm.tsx @@ -211,6 +211,15 @@ export const PasswordRegisterForm: React.FC<{ {t("frontend.register.invite_invalid")} )} + {/* The user never chose a pinned value, so its errors get their own words */} + {pinnedUsername && !!fields.username?.errors.length && ( + {t("frontend.register.pinned_username_error")} + )} + + {pinnedEmail && !!fields.email?.errors.length && ( + {t("frontend.register.pinned_email_error")} + )} + {formErrors.map((error, index) => (
{ ).toBeTruthy(); }); }); + + describe("with an invite pinning the username and email", () => { + const invite = { + valid: true, + username: "alice", + email: "alice@example.com", + passwordless: true, + }; + + it("explains an error on the username above the form", async () => { + await mountRegister({ + invite, + form: { + errors: [], + fields: { username: { value: "", errors: [{ kind: "exists" }] } }, + }, + }); + + const alert = screen.getByRole("alert"); + expect(alert).toHaveTextContent( + "The account this invite is for can't be created.", + ); + expect( + alert.compareDocumentPosition( + screen.getByRole("button", { name: "Send code" }), + ) & Node.DOCUMENT_POSITION_FOLLOWING, + ).toBeTruthy(); + }); + + it("explains an error on the email address above the form", async () => { + await mountRegister({ + invite, + form: { + errors: [], + fields: { + email: { + value: "alice@example.com", + errors: [ + { + kind: "policy", + code: "email-domain-banned", + message: "Email domain is banned", + }, + ], + }, + }, + }, + }); + + const alert = screen.getByRole("alert"); + expect(alert).toHaveTextContent( + "This invite's email address can't be used to create an account.", + ); + expect( + alert.compareDocumentPosition( + screen.getByRole("button", { name: "Send code" }), + ) & Node.DOCUMENT_POSITION_FOLLOWING, + ).toBeTruthy(); + }); + }); });