mirror of
https://github.com/bitwarden/browser.git
synced 2024-12-19 15:57:42 +01:00
[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
This commit is contained in:
parent
a049b553a6
commit
00e1c936fb
@ -13,6 +13,7 @@ import { CollectionView } from "@bitwarden/admin-console/common";
|
|||||||
import { AuditService } from "@bitwarden/common/abstractions/audit.service";
|
import { AuditService } from "@bitwarden/common/abstractions/audit.service";
|
||||||
import { EventCollectionService } from "@bitwarden/common/abstractions/event/event-collection.service";
|
import { EventCollectionService } from "@bitwarden/common/abstractions/event/event-collection.service";
|
||||||
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
|
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 { AutofillSettingsServiceAbstraction } from "@bitwarden/common/autofill/services/autofill-settings.service";
|
||||||
import { DomainSettingsService } from "@bitwarden/common/autofill/services/domain-settings.service";
|
import { DomainSettingsService } from "@bitwarden/common/autofill/services/domain-settings.service";
|
||||||
import { ClientType } from "@bitwarden/common/enums";
|
import { ClientType } from "@bitwarden/common/enums";
|
||||||
@ -183,6 +184,12 @@ export default {
|
|||||||
getClientType: () => ClientType.Browser,
|
getClientType: () => ClientType.Browser,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
provide: AccountService,
|
||||||
|
useValue: {
|
||||||
|
activeAccount$: new BehaviorSubject({ email: "test@example.com" }),
|
||||||
|
},
|
||||||
|
},
|
||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
componentWrapperDecorator(
|
componentWrapperDecorator(
|
||||||
|
@ -27,9 +27,9 @@
|
|||||||
<bit-label>{{ "owner" | i18n }}</bit-label>
|
<bit-label>{{ "owner" | i18n }}</bit-label>
|
||||||
<bit-select formControlName="organizationId">
|
<bit-select formControlName="organizationId">
|
||||||
<bit-option
|
<bit-option
|
||||||
*ngIf="allowPersonalOwnership"
|
*ngIf="showPersonalOwnerOption"
|
||||||
[value]="null"
|
[value]="null"
|
||||||
[label]="'selfOwnershipLabel' | i18n"
|
[label]="userEmail$ | async"
|
||||||
></bit-option>
|
></bit-option>
|
||||||
<bit-option
|
<bit-option
|
||||||
*ngFor="let org of config.organizations"
|
*ngFor="let org of config.organizations"
|
||||||
|
@ -1,13 +1,17 @@
|
|||||||
import { CommonModule } from "@angular/common";
|
import { CommonModule } from "@angular/common";
|
||||||
import { ComponentFixture, fakeAsync, TestBed, tick } from "@angular/core/testing";
|
import { ComponentFixture, fakeAsync, TestBed, tick } from "@angular/core/testing";
|
||||||
import { ReactiveFormsModule } from "@angular/forms";
|
import { ReactiveFormsModule } from "@angular/forms";
|
||||||
|
import { By } from "@angular/platform-browser";
|
||||||
import { mock, MockProxy } from "jest-mock-extended";
|
import { mock, MockProxy } from "jest-mock-extended";
|
||||||
|
import { BehaviorSubject } from "rxjs";
|
||||||
|
|
||||||
import { CollectionView } from "@bitwarden/admin-console/common";
|
import { CollectionView } from "@bitwarden/admin-console/common";
|
||||||
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
|
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 { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||||
import { CollectionId, OrganizationId } from "@bitwarden/common/types/guid";
|
import { CollectionId, OrganizationId } from "@bitwarden/common/types/guid";
|
||||||
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
|
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
|
||||||
|
import { SelectComponent } from "@bitwarden/components";
|
||||||
|
|
||||||
import { CipherFormConfig } from "../../abstractions/cipher-form-config.service";
|
import { CipherFormConfig } from "../../abstractions/cipher-form-config.service";
|
||||||
import { CipherFormContainer } from "../../cipher-form-container";
|
import { CipherFormContainer } from "../../cipher-form-container";
|
||||||
@ -20,6 +24,8 @@ describe("ItemDetailsSectionComponent", () => {
|
|||||||
let cipherFormProvider: MockProxy<CipherFormContainer>;
|
let cipherFormProvider: MockProxy<CipherFormContainer>;
|
||||||
let i18nService: MockProxy<I18nService>;
|
let i18nService: MockProxy<I18nService>;
|
||||||
|
|
||||||
|
const activeAccount$ = new BehaviorSubject<{ email: string }>({ email: "test@example.com" });
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
cipherFormProvider = mock<CipherFormContainer>();
|
cipherFormProvider = mock<CipherFormContainer>();
|
||||||
i18nService = mock<I18nService>();
|
i18nService = mock<I18nService>();
|
||||||
@ -29,6 +35,7 @@ describe("ItemDetailsSectionComponent", () => {
|
|||||||
providers: [
|
providers: [
|
||||||
{ provide: CipherFormContainer, useValue: cipherFormProvider },
|
{ provide: CipherFormContainer, useValue: cipherFormProvider },
|
||||||
{ provide: I18nService, useValue: i18nService },
|
{ provide: I18nService, useValue: i18nService },
|
||||||
|
{ provide: AccountService, useValue: { activeAccount$ } },
|
||||||
],
|
],
|
||||||
}).compileComponents();
|
}).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", () => {
|
describe("showOwnership", () => {
|
||||||
it("should return true if ownership change is allowed or in edit mode with at least one organization", () => {
|
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);
|
jest.spyOn(component, "allowOwnershipChange", "get").mockReturnValue(true);
|
||||||
|
@ -7,6 +7,7 @@ import { concatMap, map } from "rxjs";
|
|||||||
import { CollectionView } from "@bitwarden/admin-console/common";
|
import { CollectionView } from "@bitwarden/admin-console/common";
|
||||||
import { JslibModule } from "@bitwarden/angular/jslib.module";
|
import { JslibModule } from "@bitwarden/angular/jslib.module";
|
||||||
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
|
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 { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||||
import { CollectionId, OrganizationId } from "@bitwarden/common/types/guid";
|
import { CollectionId, OrganizationId } from "@bitwarden/common/types/guid";
|
||||||
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
|
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
|
||||||
@ -68,6 +69,9 @@ export class ItemDetailsSectionComponent implements OnInit {
|
|||||||
|
|
||||||
protected showCollectionsControl: boolean;
|
protected showCollectionsControl: boolean;
|
||||||
|
|
||||||
|
/** The email address associated with the active account */
|
||||||
|
protected userEmail$ = this.accountService.activeAccount$.pipe(map((account) => account.email));
|
||||||
|
|
||||||
@Input({ required: true })
|
@Input({ required: true })
|
||||||
config: CipherFormConfig;
|
config: CipherFormConfig;
|
||||||
|
|
||||||
@ -96,11 +100,23 @@ export class ItemDetailsSectionComponent implements OnInit {
|
|||||||
return this.config.initialValues;
|
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(
|
constructor(
|
||||||
private cipherFormContainer: CipherFormContainer,
|
private cipherFormContainer: CipherFormContainer,
|
||||||
private formBuilder: FormBuilder,
|
private formBuilder: FormBuilder,
|
||||||
private i18nService: I18nService,
|
private i18nService: I18nService,
|
||||||
private destroyRef: DestroyRef,
|
private destroyRef: DestroyRef,
|
||||||
|
private accountService: AccountService,
|
||||||
) {
|
) {
|
||||||
this.cipherFormContainer.registerChildForm("itemDetails", this.itemDetailsForm);
|
this.cipherFormContainer.registerChildForm("itemDetails", this.itemDetailsForm);
|
||||||
this.itemDetailsForm.valueChanges
|
this.itemDetailsForm.valueChanges
|
||||||
|
Loading…
Reference in New Issue
Block a user