From 00e1c936fbd4211bab09287df0eb6287d2523316 Mon Sep 17 00:00:00 2001 From: Nick Krantz <125900171+nick-livefront@users.noreply.github.com> Date: Fri, 1 Nov 2024 11:05:02 -0500 Subject: [PATCH] [PM-13928]use the user's email address in owner dropdown rather than "You" (#11798) * use the user's email address in owner dropdown rather than "You" * show ownership value in individual vault when disabled * import account service in storybook --- .../src/cipher-form/cipher-form.stories.ts | 7 ++++ .../item-details-section.component.html | 4 +-- .../item-details-section.component.spec.ts | 36 +++++++++++++++++++ .../item-details-section.component.ts | 16 +++++++++ 4 files changed, 61 insertions(+), 2 deletions(-) diff --git a/libs/vault/src/cipher-form/cipher-form.stories.ts b/libs/vault/src/cipher-form/cipher-form.stories.ts index 13f233f53d..e48cf384c2 100644 --- a/libs/vault/src/cipher-form/cipher-form.stories.ts +++ b/libs/vault/src/cipher-form/cipher-form.stories.ts @@ -13,6 +13,7 @@ import { CollectionView } from "@bitwarden/admin-console/common"; import { AuditService } from "@bitwarden/common/abstractions/audit.service"; import { EventCollectionService } from "@bitwarden/common/abstractions/event/event-collection.service"; import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; +import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { AutofillSettingsServiceAbstraction } from "@bitwarden/common/autofill/services/autofill-settings.service"; import { DomainSettingsService } from "@bitwarden/common/autofill/services/domain-settings.service"; import { ClientType } from "@bitwarden/common/enums"; @@ -183,6 +184,12 @@ export default { getClientType: () => ClientType.Browser, }, }, + { + provide: AccountService, + useValue: { + activeAccount$: new BehaviorSubject({ email: "test@example.com" }), + }, + }, ], }), componentWrapperDecorator( diff --git a/libs/vault/src/cipher-form/components/item-details/item-details-section.component.html b/libs/vault/src/cipher-form/components/item-details/item-details-section.component.html index 6c6bd8a801..648539932d 100644 --- a/libs/vault/src/cipher-form/components/item-details/item-details-section.component.html +++ b/libs/vault/src/cipher-form/components/item-details/item-details-section.component.html @@ -27,9 +27,9 @@ {{ "owner" | i18n }} { let cipherFormProvider: MockProxy; let i18nService: MockProxy; + const activeAccount$ = new BehaviorSubject<{ email: string }>({ email: "test@example.com" }); + beforeEach(async () => { cipherFormProvider = mock(); i18nService = mock(); @@ -29,6 +35,7 @@ describe("ItemDetailsSectionComponent", () => { providers: [ { provide: CipherFormContainer, useValue: cipherFormProvider }, { provide: I18nService, useValue: i18nService }, + { provide: AccountService, useValue: { activeAccount$ } }, ], }).compileComponents(); @@ -207,6 +214,35 @@ describe("ItemDetailsSectionComponent", () => { }); }); + describe("showPersonalOwnerOption", () => { + it("should show personal ownership when the configuration allows", () => { + component.config.mode = "edit"; + component.config.allowPersonalOwnership = true; + component.config.organizations = [{ id: "134-433-22" } as Organization]; + fixture.detectChanges(); + + const select = fixture.debugElement.query(By.directive(SelectComponent)); + const { value, label } = select.componentInstance.items[0]; + + expect(value).toBeNull(); + expect(label).toBe("test@example.com"); + }); + + it("should show personal ownership when the control is disabled", async () => { + component.config.mode = "edit"; + component.config.allowPersonalOwnership = false; + component.config.organizations = [{ id: "134-433-22" } as Organization]; + await component.ngOnInit(); + fixture.detectChanges(); + + const select = fixture.debugElement.query(By.directive(SelectComponent)); + + const { value, label } = select.componentInstance.items[0]; + expect(value).toBeNull(); + expect(label).toBe("test@example.com"); + }); + }); + describe("showOwnership", () => { it("should return true if ownership change is allowed or in edit mode with at least one organization", () => { jest.spyOn(component, "allowOwnershipChange", "get").mockReturnValue(true); diff --git a/libs/vault/src/cipher-form/components/item-details/item-details-section.component.ts b/libs/vault/src/cipher-form/components/item-details/item-details-section.component.ts index 06ce363a27..fb193dd3dd 100644 --- a/libs/vault/src/cipher-form/components/item-details/item-details-section.component.ts +++ b/libs/vault/src/cipher-form/components/item-details/item-details-section.component.ts @@ -7,6 +7,7 @@ import { concatMap, map } from "rxjs"; import { CollectionView } from "@bitwarden/admin-console/common"; import { JslibModule } from "@bitwarden/angular/jslib.module"; import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; +import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { CollectionId, OrganizationId } from "@bitwarden/common/types/guid"; import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; @@ -68,6 +69,9 @@ export class ItemDetailsSectionComponent implements OnInit { protected showCollectionsControl: boolean; + /** The email address associated with the active account */ + protected userEmail$ = this.accountService.activeAccount$.pipe(map((account) => account.email)); + @Input({ required: true }) config: CipherFormConfig; @@ -96,11 +100,23 @@ export class ItemDetailsSectionComponent implements OnInit { return this.config.initialValues; } + /** + * Show the personal ownership option in the Owner dropdown when: + * - Personal ownership is allowed + * - The `organizationId` control is disabled. This avoids the scenario + * where a the dropdown is empty because the user personally owns the cipher + * but cannot edit the ownership. + */ + get showPersonalOwnerOption() { + return this.allowPersonalOwnership || !this.itemDetailsForm.controls.organizationId.enabled; + } + constructor( private cipherFormContainer: CipherFormContainer, private formBuilder: FormBuilder, private i18nService: I18nService, private destroyRef: DestroyRef, + private accountService: AccountService, ) { this.cipherFormContainer.registerChildForm("itemDetails", this.itemDetailsForm); this.itemDetailsForm.valueChanges