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 c68df5bbfa..40a8954b05 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,7 +27,7 @@ [label]="userEmail$ | async" > diff --git a/libs/vault/src/cipher-form/components/item-details/item-details-section.component.spec.ts b/libs/vault/src/cipher-form/components/item-details/item-details-section.component.spec.ts index 3995422944..aa68770774 100644 --- a/libs/vault/src/cipher-form/components/item-details/item-details-section.component.spec.ts +++ b/libs/vault/src/cipher-form/components/item-details/item-details-section.component.spec.ts @@ -59,6 +59,9 @@ describe("ItemDetailsSectionComponent", () => { initializedWithCachedCipher, }); i18nService = mock(); + i18nService.collator = { + compare: (a: string, b: string) => a.localeCompare(b), + } as Intl.Collator; await TestBed.configureTestingModule({ imports: [ItemDetailsSectionComponent, CommonModule, ReactiveFormsModule], @@ -184,16 +187,18 @@ describe("ItemDetailsSectionComponent", () => { it("should allow ownership change if personal ownership is allowed and there is at least one organization", () => { component.config.allowPersonalOwnership = true; - component.config.organizations = [{ id: "org1" } as Organization]; + component.config.organizations = [{ id: "org1", name: "org1" } as Organization]; + fixture.detectChanges(); expect(component.allowOwnershipChange).toBe(true); }); it("should allow ownership change if personal ownership is not allowed but there is more than one organization", () => { component.config.allowPersonalOwnership = false; component.config.organizations = [ - { id: "org1" } as Organization, - { id: "org2" } as Organization, + { id: "org1", name: "org1" } as Organization, + { id: "org2", name: "org2" } as Organization, ]; + fixture.detectChanges(); expect(component.allowOwnershipChange).toBe(true); }); }); @@ -206,7 +211,8 @@ describe("ItemDetailsSectionComponent", () => { it("should return the first organization id if personal ownership is not allowed", () => { component.config.allowPersonalOwnership = false; - component.config.organizations = [{ id: "org1" } as Organization]; + component.config.organizations = [{ id: "org1", name: "Organization 1" } as Organization]; + fixture.detectChanges(); expect(component.defaultOwner).toBe("org1"); }); }); @@ -250,6 +256,7 @@ describe("ItemDetailsSectionComponent", () => { jest.spyOn(component, "allowOwnershipChange", "get").mockReturnValue(false); component.config.mode = "edit"; component.config.organizations = [{ id: "org1" } as Organization]; + fixture.detectChanges(); expect(component.showOwnership).toBe(true); }); @@ -322,8 +329,8 @@ describe("ItemDetailsSectionComponent", () => { it("should select the first organization if personal ownership is not allowed", async () => { component.config.allowPersonalOwnership = false; component.config.organizations = [ - { id: "org1" } as Organization, - { id: "org2" } as Organization, + { id: "org1", name: "org1" } as Organization, + { id: "org2", name: "org2" } as Organization, ]; component.originalCipherView = { name: "cipher1", @@ -517,4 +524,23 @@ describe("ItemDetailsSectionComponent", () => { expect(component["readOnlyCollectionsNames"]).toEqual(["Collection 1", "Collection 3"]); }); }); + + describe("organizationOptions", () => { + it("should sort the organizations by name", async () => { + component.config.mode = "edit"; + component.config.organizations = [ + { id: "org2", name: "org2" } as Organization, + { id: "org1", name: "org1" } as Organization, + ]; + component.originalCipherView = {} as CipherView; + + await component.ngOnInit(); + fixture.detectChanges(); + + const select = fixture.debugElement.query(By.directive(SelectComponent)); + const { label } = select.componentInstance.items[0]; + + expect(label).toBe("org1"); + }); + }); }); 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 50bafd48b4..dcbc4e8c92 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 @@ -12,6 +12,7 @@ import { OrganizationUserType } from "@bitwarden/common/admin-console/enums"; 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 { Utils } from "@bitwarden/common/platform/misc/utils"; import { CollectionId, OrganizationId } from "@bitwarden/common/types/guid"; import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; import { @@ -74,6 +75,8 @@ export class ItemDetailsSectionComponent implements OnInit { /** The email address associated with the active account */ protected userEmail$ = this.accountService.activeAccount$.pipe(map((account) => account.email)); + protected organizations: Organization[] = []; + @Input({ required: true }) config: CipherFormConfig; @@ -90,10 +93,6 @@ export class ItemDetailsSectionComponent implements OnInit { return this.config.mode === "partial-edit"; } - get organizations(): Organization[] { - return this.config.organizations; - } - get allowPersonalOwnership() { return this.config.allowPersonalOwnership; } @@ -186,6 +185,10 @@ export class ItemDetailsSectionComponent implements OnInit { } async ngOnInit() { + this.organizations = this.config.organizations.sort( + Utils.getSortFunction(this.i18nService, "name"), + ); + if (!this.allowPersonalOwnership && this.organizations.length === 0) { throw new Error("No organizations available for ownership."); }