From 263ec9412433f1c87360b5810184e4e43fd3d5d2 Mon Sep 17 00:00:00 2001 From: John Harrington <84741727+harr1424@users.noreply.github.com> Date: Wed, 18 Feb 2026 14:59:34 -0700 Subject: [PATCH] [PM-32161] Remove all emails when email list field is cleared and send is saved (#18959) * add new validation criteria to prevent authType.Email with an empty emails field * simplify validation logic --- apps/browser/src/_locales/en/messages.json | 4 +- apps/desktop/src/locales/en/messages.json | 3 ++ apps/web/src/locales/en/messages.json | 3 ++ .../send-details.component.spec.ts | 53 +++++++++++++++++++ .../send-details/send-details.component.ts | 22 +++++++- 5 files changed, 83 insertions(+), 2 deletions(-) diff --git a/apps/browser/src/_locales/en/messages.json b/apps/browser/src/_locales/en/messages.json index 5ed97ce0f07..cc99e0abe18 100644 --- a/apps/browser/src/_locales/en/messages.json +++ b/apps/browser/src/_locales/en/messages.json @@ -6160,10 +6160,12 @@ "enterMultipleEmailsSeparatedByComma": { "message": "Enter multiple emails by separating with a comma." }, + "emailsRequiredChangeAccessType": { + "message": "Email verification requires at least one email address. To remove all emails, change the access type above." + }, "emailPlaceholder": { "message": "user@bitwarden.com , user@acme.com" }, - "downloadBitwardenApps": { "message": "Download Bitwarden apps" }, diff --git a/apps/desktop/src/locales/en/messages.json b/apps/desktop/src/locales/en/messages.json index 85ef3d94001..3f005db0ba8 100644 --- a/apps/desktop/src/locales/en/messages.json +++ b/apps/desktop/src/locales/en/messages.json @@ -4615,6 +4615,9 @@ "enterMultipleEmailsSeparatedByComma": { "message": "Enter multiple emails by separating with a comma." }, + "emailsRequiredChangeAccessType": { + "message": "Email verification requires at least one email address. To remove all emails, change the access type above." + }, "emailPlaceholder": { "message": "user@bitwarden.com , user@acme.com" }, diff --git a/apps/web/src/locales/en/messages.json b/apps/web/src/locales/en/messages.json index 4731be36ef5..ba59184a9f9 100644 --- a/apps/web/src/locales/en/messages.json +++ b/apps/web/src/locales/en/messages.json @@ -12866,6 +12866,9 @@ "enterMultipleEmailsSeparatedByComma": { "message": "Enter multiple emails by separating with a comma." }, + "emailsRequiredChangeAccessType": { + "message": "Email verification requires at least one email address. To remove all emails, change the access type above." + }, "emailPlaceholder": { "message": "user@bitwarden.com , user@acme.com" }, diff --git a/libs/tools/send/send-ui/src/send-form/components/send-details/send-details.component.spec.ts b/libs/tools/send/send-ui/src/send-form/components/send-details/send-details.component.spec.ts index f816c9d5ce4..43b2bc7bcd5 100644 --- a/libs/tools/send/send-ui/src/send-form/components/send-details/send-details.component.spec.ts +++ b/libs/tools/send/send-ui/src/send-form/components/send-details/send-details.component.spec.ts @@ -127,4 +127,57 @@ describe("SendDetailsComponent", () => { expect(emailsControl?.validator).toBeNull(); expect(passwordControl?.validator).toBeNull(); }); + + it("should show validation error when emails are cleared while authType is Email", () => { + // Set authType to Email with valid emails + component.sendDetailsForm.patchValue({ + authType: AuthType.Email, + emails: "test@example.com", + }); + expect(component.sendDetailsForm.get("emails")?.valid).toBe(true); + + // Clear emails - should trigger validation error + component.sendDetailsForm.patchValue({ emails: "" }); + expect(component.sendDetailsForm.get("emails")?.valid).toBe(false); + expect(component.sendDetailsForm.get("emails")?.hasError("emailsRequiredForEmailAuth")).toBe( + true, + ); + }); + + it("should clear validation error when authType is changed from Email after clearing emails", () => { + // Set authType to Email and then clear emails + component.sendDetailsForm.patchValue({ + authType: AuthType.Email, + emails: "test@example.com", + }); + component.sendDetailsForm.patchValue({ emails: "" }); + expect(component.sendDetailsForm.get("emails")?.valid).toBe(false); + + // Change authType to None - emails field should become valid (no longer required) + component.sendDetailsForm.patchValue({ authType: AuthType.None }); + expect(component.sendDetailsForm.get("emails")?.valid).toBe(true); + }); + + it("should force user to change authType by blocking form submission when emails are cleared", () => { + // Set up a send with email verification + component.sendDetailsForm.patchValue({ + name: "Test Send", + authType: AuthType.Email, + emails: "user@example.com", + }); + expect(component.sendDetailsForm.valid).toBe(true); + + // User clears emails field + component.sendDetailsForm.patchValue({ emails: "" }); + + // Form should now be invalid, preventing save + expect(component.sendDetailsForm.valid).toBe(false); + expect(component.sendDetailsForm.get("emails")?.hasError("emailsRequiredForEmailAuth")).toBe( + true, + ); + + // User must change authType to continue + component.sendDetailsForm.patchValue({ authType: AuthType.None }); + expect(component.sendDetailsForm.valid).toBe(true); + }); }); diff --git a/libs/tools/send/send-ui/src/send-form/components/send-details/send-details.component.ts b/libs/tools/send/send-ui/src/send-form/components/send-details/send-details.component.ts index ac1453a925c..78681a70a00 100644 --- a/libs/tools/send/send-ui/src/send-form/components/send-details/send-details.component.ts +++ b/libs/tools/send/send-ui/src/send-form/components/send-details/send-details.component.ts @@ -224,7 +224,10 @@ export class SendDetailsComponent implements OnInit { } else if (type === AuthType.Email) { passwordControl.setValue(null); passwordControl.clearValidators(); - emailsControl.setValidators([Validators.required, this.emailListValidator()]); + emailsControl.setValidators([ + this.emailsRequiredForEmailAuthValidator(), + this.emailListValidator(), + ]); } else { emailsControl.setValue(null); emailsControl.clearValidators(); @@ -317,6 +320,23 @@ export class SendDetailsComponent implements OnInit { }; } + emailsRequiredForEmailAuthValidator(): ValidatorFn { + return (control: FormControl): ValidationErrors | null => { + const authType = this.sendDetailsForm?.get("authType")?.value; + const emails = control.value; + + if (authType === AuthType.Email && (!emails || emails.trim() === "")) { + return { + emailsRequiredForEmailAuth: { + message: this.i18nService.t("emailsRequiredChangeAccessType"), + }, + }; + } + + return null; + }; + } + generatePassword = async () => { const on$ = new BehaviorSubject({ source: "send", type: Type.password }); const account$ = this.accountService.activeAccount$.pipe(