From bd2479d2c2fae45a90b7e155e351ea4bc00c8725 Mon Sep 17 00:00:00 2001 From: Brandon Treston Date: Mon, 1 Dec 2025 10:20:28 -0500 Subject: [PATCH] [PM-28350] Refactor policies component (#17636) * refactor policies component * add tests * cleanup * clean up * change trackBy to * change detetction --- .../policies/policies.component.html | 33 +- .../policies/policies.component.spec.ts | 498 ++++++++++++++++++ .../policies/policies.component.ts | 157 +++--- .../auto-confirm-policy.component.ts | 13 +- .../autotype-policy.component.ts | 6 +- .../disable-send.component.ts | 6 +- .../master-password.component.ts | 6 +- .../organization-data-ownership.component.ts | 6 +- .../password-generator.component.ts | 6 +- .../remove-unlock-with-pin.component.ts | 6 +- .../require-sso.component.ts | 6 +- .../reset-password.component.ts | 6 +- .../restricted-item-types.component.ts | 6 +- .../send-options.component.ts | 6 +- .../single-org.component.ts | 6 +- .../two-factor-authentication.component.ts | 6 +- .../uri-match-default.component.ts | 1 + ...t-organization-data-ownership.component.ts | 6 +- .../activate-autofill.component.ts | 1 + .../automatic-app-login.component.ts | 1 + ...aimed-domain-account-creation.component.ts | 1 + ...disable-personal-vault-export.component.ts | 1 + 22 files changed, 643 insertions(+), 141 deletions(-) create mode 100644 apps/web/src/app/admin-console/organizations/policies/policies.component.spec.ts diff --git a/apps/web/src/app/admin-console/organizations/policies/policies.component.html b/apps/web/src/app/admin-console/organizations/policies/policies.component.html index 8df73a50e14..c38092146ab 100644 --- a/apps/web/src/app/admin-console/organizations/policies/policies.component.html +++ b/apps/web/src/app/admin-console/organizations/policies/policies.component.html @@ -1,27 +1,34 @@ +@let organization = organization$ | async; +@let policiesEnabledMap = policiesEnabledMap$ | async; +@let organizationId = organizationId$ | async; + - @if (loading) { + @if (!organization || !policiesEnabledMap || !organizationId) { {{ "loading" | i18n }} - } - @if (!loading) { + } @else { - @for (p of policies$ | async; track p.type) { - - - - @if (policiesEnabledMap.get(p.type)) { - {{ "on" | i18n }} - } - {{ p.description | i18n }} - - + @for (p of policies$ | async; track $index) { + @if (p.display$(organization, configService) | async) { + + + + @if (policiesEnabledMap.get(p.type)) { + {{ "on" | i18n }} + } + {{ p.description | i18n }} + + + } } diff --git a/apps/web/src/app/admin-console/organizations/policies/policies.component.spec.ts b/apps/web/src/app/admin-console/organizations/policies/policies.component.spec.ts new file mode 100644 index 00000000000..0e025a9d52a --- /dev/null +++ b/apps/web/src/app/admin-console/organizations/policies/policies.component.spec.ts @@ -0,0 +1,498 @@ +import { NO_ERRORS_SCHEMA } from "@angular/core"; +import { ComponentFixture, TestBed } from "@angular/core/testing"; +import { ActivatedRoute } from "@angular/router"; +import { mock, MockProxy } from "jest-mock-extended"; +import { BehaviorSubject, of, firstValueFrom } from "rxjs"; + +import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; +import { PolicyApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/policy/policy-api.service.abstraction"; +import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; +import { PolicyType } from "@bitwarden/common/admin-console/enums"; +import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; +import { PolicyResponse } from "@bitwarden/common/admin-console/models/response/policy.response"; +import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; +import { ListResponse } from "@bitwarden/common/models/response/list.response"; +import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; +import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; +import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; +import { FakeAccountService, mockAccountServiceWith } from "@bitwarden/common/spec"; +import { OrganizationId, UserId } from "@bitwarden/common/types/guid"; +import { DialogService } from "@bitwarden/components"; +import { newGuid } from "@bitwarden/guid"; + +import { BasePolicyEditDefinition } from "./base-policy-edit.component"; +import { PoliciesComponent } from "./policies.component"; +import { SingleOrgPolicy } from "./policy-edit-definitions/single-org.component"; +import { PolicyEditDialogComponent } from "./policy-edit-dialog.component"; +import { PolicyListService } from "./policy-list.service"; +import { POLICY_EDIT_REGISTER } from "./policy-register-token"; + +describe("PoliciesComponent", () => { + let component: PoliciesComponent; + let fixture: ComponentFixture; + + let mockActivatedRoute: ActivatedRoute; + let mockOrganizationService: MockProxy; + let mockAccountService: FakeAccountService; + let mockPolicyApiService: MockProxy; + let mockPolicyListService: MockProxy; + let mockDialogService: MockProxy; + let mockPolicyService: MockProxy; + let mockConfigService: MockProxy; + let mockI18nService: MockProxy; + let mockPlatformUtilsService: MockProxy; + + let routeParamsSubject: BehaviorSubject; + let queryParamsSubject: BehaviorSubject; + + const mockUserId = newGuid() as UserId; + const mockOrgId = newGuid() as OrganizationId; + const mockOrg = { + id: mockOrgId, + name: "Test Organization", + enabled: true, + } as Organization; + + const mockPolicyResponse = { + id: newGuid(), + enabled: true, + object: "policy", + organizationId: mockOrgId, + type: PolicyType.SingleOrg, + data: null, + }; + + const mockPolicy = new SingleOrgPolicy(); + + beforeEach(async () => { + routeParamsSubject = new BehaviorSubject({ organizationId: mockOrgId }); + queryParamsSubject = new BehaviorSubject({}); + + mockActivatedRoute = { + params: routeParamsSubject.asObservable(), + queryParams: queryParamsSubject.asObservable(), + } as any; + + mockOrganizationService = mock(); + mockOrganizationService.organizations$.mockReturnValue(of([mockOrg])); + + mockAccountService = mockAccountServiceWith(mockUserId); + + mockPolicyApiService = mock(); + mockPolicyApiService.getPolicies.mockResolvedValue( + new ListResponse({ Data: [mockPolicyResponse], ContinuationToken: null }, PolicyResponse), + ); + + mockPolicyListService = mock(); + mockPolicyListService.getPolicies.mockReturnValue([mockPolicy]); + + mockDialogService = mock(); + mockDialogService.open.mockReturnValue({ close: jest.fn() } as any); + + mockPolicyService = mock(); + mockPolicyService.policies$.mockReturnValue(of([])); + + mockConfigService = mock(); + mockI18nService = mock(); + mockPlatformUtilsService = mock(); + + jest.spyOn(PolicyEditDialogComponent, "open").mockReturnValue({ close: jest.fn() } as any); + + await TestBed.configureTestingModule({ + imports: [PoliciesComponent], + providers: [ + { provide: ActivatedRoute, useValue: mockActivatedRoute }, + { provide: OrganizationService, useValue: mockOrganizationService }, + { provide: AccountService, useValue: mockAccountService }, + { provide: PolicyApiServiceAbstraction, useValue: mockPolicyApiService }, + { provide: PolicyListService, useValue: mockPolicyListService }, + { provide: DialogService, useValue: mockDialogService }, + { provide: PolicyService, useValue: mockPolicyService }, + { provide: ConfigService, useValue: mockConfigService }, + { provide: I18nService, useValue: mockI18nService }, + { provide: PlatformUtilsService, useValue: mockPlatformUtilsService }, + { provide: POLICY_EDIT_REGISTER, useValue: [] }, + ], + schemas: [NO_ERRORS_SCHEMA], + }) + .overrideComponent(PoliciesComponent, { + remove: { imports: [] }, + add: { template: "
" }, + }) + .compileComponents(); + + fixture = TestBed.createComponent(PoliciesComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + afterEach(() => { + if (fixture) { + fixture.destroy(); + } + jest.restoreAllMocks(); + }); + + it("should create", () => { + expect(component).toBeTruthy(); + }); + + describe("organizationId$", () => { + it("should extract organizationId from route params", async () => { + const orgId = await firstValueFrom(component.organizationId$); + expect(orgId).toBe(mockOrgId); + }); + + it("should emit new organizationId when route params change", (done) => { + const newOrgId = newGuid() as OrganizationId; + const emittedValues: OrganizationId[] = []; + + const subscription = component.organizationId$.subscribe((orgId) => { + emittedValues.push(orgId); + + if (emittedValues.length === 2) { + expect(emittedValues[0]).toBe(mockOrgId); + expect(emittedValues[1]).toBe(newOrgId); + subscription.unsubscribe(); + done(); + } + }); + + routeParamsSubject.next({ organizationId: newOrgId }); + }); + }); + + describe("organization$", () => { + it("should retrieve organization for current user and organizationId", async () => { + const org = await firstValueFrom(component.organization$); + expect(org).toBe(mockOrg); + expect(mockOrganizationService.organizations$).toHaveBeenCalledWith(mockUserId); + }); + + it("should throw error when organization is not found", async () => { + mockOrganizationService.organizations$.mockReturnValue(of([])); + + await expect(firstValueFrom(component.organization$)).rejects.toThrow( + "No organization found for provided userId", + ); + }); + }); + + describe("policies$", () => { + it("should return policies from PolicyListService", async () => { + const policies = await firstValueFrom(component.policies$); + + expect(policies).toBeDefined(); + expect(Array.isArray(policies)).toBe(true); + }); + }); + + describe("orgPolicies$", () => { + it("should fetch policies from API for current organization", async () => { + const mockPolicyResponsesData = [ + { + id: newGuid(), + organizationId: mockOrgId, + type: PolicyType.TwoFactorAuthentication, + enabled: true, + data: null, + }, + { + id: newGuid(), + organizationId: mockOrgId, + type: PolicyType.RequireSso, + enabled: false, + data: null, + }, + ]; + + const listResponse = new ListResponse( + { Data: mockPolicyResponsesData, ContinuationToken: null }, + PolicyResponse, + ); + + mockPolicyApiService.getPolicies.mockResolvedValue(listResponse); + + const policies = await firstValueFrom(component["orgPolicies$"]); + expect(policies).toEqual(listResponse.data); + expect(mockPolicyApiService.getPolicies).toHaveBeenCalledWith(mockOrgId); + }); + + it("should return empty array when API returns no data", async () => { + mockPolicyApiService.getPolicies.mockResolvedValue( + new ListResponse({ Data: [], ContinuationToken: null }, PolicyResponse), + ); + + const policies = await firstValueFrom(component["orgPolicies$"]); + expect(policies).toEqual([]); + }); + + it("should return empty array when API returns null data", async () => { + mockPolicyApiService.getPolicies.mockResolvedValue( + new ListResponse({ Data: null, ContinuationToken: null }, PolicyResponse), + ); + + const policies = await firstValueFrom(component["orgPolicies$"]); + expect(policies).toEqual([]); + }); + }); + + describe("policiesEnabledMap$", () => { + it("should create a map of policy types to their enabled status", async () => { + const mockPolicyResponsesData = [ + { + id: "policy-1", + organizationId: mockOrgId, + type: PolicyType.TwoFactorAuthentication, + enabled: true, + data: null, + }, + { + id: "policy-2", + organizationId: mockOrgId, + type: PolicyType.RequireSso, + enabled: false, + data: null, + }, + { + id: "policy-3", + organizationId: mockOrgId, + type: PolicyType.SingleOrg, + enabled: true, + data: null, + }, + ]; + + mockPolicyApiService.getPolicies.mockResolvedValue( + new ListResponse( + { Data: mockPolicyResponsesData, ContinuationToken: null }, + PolicyResponse, + ), + ); + + const map = await firstValueFrom(component.policiesEnabledMap$); + expect(map.size).toBe(3); + expect(map.get(PolicyType.TwoFactorAuthentication)).toBe(true); + expect(map.get(PolicyType.RequireSso)).toBe(false); + expect(map.get(PolicyType.SingleOrg)).toBe(true); + }); + + it("should create empty map when no policies exist", async () => { + mockPolicyApiService.getPolicies.mockResolvedValue( + new ListResponse({ Data: [], ContinuationToken: null }, PolicyResponse), + ); + + const map = await firstValueFrom(component.policiesEnabledMap$); + expect(map.size).toBe(0); + }); + }); + + describe("constructor subscription", () => { + it("should subscribe to policyService.policies$ on initialization", () => { + expect(mockPolicyService.policies$).toHaveBeenCalledWith(mockUserId); + }); + + it("should refresh policies when policyService emits", async () => { + const policiesSubject = new BehaviorSubject([]); + mockPolicyService.policies$.mockReturnValue(policiesSubject.asObservable()); + + let callCount = 0; + mockPolicyApiService.getPolicies.mockImplementation(() => { + callCount++; + return of(new ListResponse({ Data: [], ContinuationToken: null }, PolicyResponse)); + }); + + const newFixture = TestBed.createComponent(PoliciesComponent); + newFixture.detectChanges(); + + const initialCallCount = callCount; + + policiesSubject.next([{ type: PolicyType.TwoFactorAuthentication }]); + + expect(callCount).toBeGreaterThan(initialCallCount); + + newFixture.destroy(); + }); + }); + + describe("handleLaunchEvent", () => { + it("should open policy dialog when policyId is in query params", async () => { + const mockPolicyId = newGuid(); + const mockPolicy: BasePolicyEditDefinition = { + name: "Test Policy", + description: "Test Description", + type: PolicyType.TwoFactorAuthentication, + component: {} as any, + showDescription: true, + display$: () => of(true), + }; + + const mockPolicyResponseData = { + id: mockPolicyId, + organizationId: mockOrgId, + type: PolicyType.TwoFactorAuthentication, + enabled: true, + data: null, + }; + + queryParamsSubject.next({ policyId: mockPolicyId }); + + mockPolicyApiService.getPolicies.mockReturnValue( + of( + new ListResponse( + { Data: [mockPolicyResponseData], ContinuationToken: null }, + PolicyResponse, + ), + ), + ); + + const dialogOpenSpy = jest + .spyOn(PolicyEditDialogComponent, "open") + .mockReturnValue({ close: jest.fn() } as any); + + TestBed.resetTestingModule(); + await TestBed.configureTestingModule({ + imports: [PoliciesComponent], + providers: [ + { provide: ActivatedRoute, useValue: mockActivatedRoute }, + { provide: OrganizationService, useValue: mockOrganizationService }, + { provide: AccountService, useValue: mockAccountService }, + { provide: PolicyApiServiceAbstraction, useValue: mockPolicyApiService }, + { provide: PolicyListService, useValue: mockPolicyListService }, + { provide: DialogService, useValue: mockDialogService }, + { provide: PolicyService, useValue: mockPolicyService }, + { provide: ConfigService, useValue: mockConfigService }, + { provide: I18nService, useValue: mockI18nService }, + { provide: PlatformUtilsService, useValue: mockPlatformUtilsService }, + { provide: POLICY_EDIT_REGISTER, useValue: [mockPolicy] }, + ], + schemas: [NO_ERRORS_SCHEMA], + }) + .overrideComponent(PoliciesComponent, { + remove: { imports: [] }, + add: { template: "
" }, + }) + .compileComponents(); + + const newFixture = TestBed.createComponent(PoliciesComponent); + newFixture.detectChanges(); + + expect(dialogOpenSpy).toHaveBeenCalled(); + const callArgs = dialogOpenSpy.mock.calls[0][1]; + expect(callArgs.data?.policy.type).toBe(mockPolicy.type); + expect(callArgs.data?.organizationId).toBe(mockOrgId); + + newFixture.destroy(); + }); + + it("should not open dialog when policyId is not in query params", async () => { + const editSpy = jest.spyOn(component, "edit"); + + queryParamsSubject.next({}); + + expect(editSpy).not.toHaveBeenCalled(); + }); + + it("should not open dialog when policyId does not match any org policy", async () => { + const mockPolicy: BasePolicyEditDefinition = { + name: "Test Policy", + description: "Test Description", + type: PolicyType.TwoFactorAuthentication, + component: {} as any, + showDescription: true, + display$: () => of(true), + }; + + mockPolicyListService.getPolicies.mockReturnValue([mockPolicy]); + mockPolicyApiService.getPolicies.mockResolvedValue( + new ListResponse({ Data: [], ContinuationToken: null }, PolicyResponse), + ); + + const editSpy = jest.spyOn(component, "edit"); + + queryParamsSubject.next({ policyId: "non-existent-policy-id" }); + + expect(editSpy).not.toHaveBeenCalled(); + }); + }); + + describe("edit", () => { + it("should call dialogService.open with correct parameters when no custom dialog is specified", () => { + const mockPolicy: BasePolicyEditDefinition = { + name: "Test Policy", + description: "Test Description", + type: PolicyType.TwoFactorAuthentication, + component: {} as any, + showDescription: true, + display$: () => of(true), + }; + + const openSpy = jest.spyOn(PolicyEditDialogComponent, "open"); + + component.edit(mockPolicy, mockOrgId); + + expect(openSpy).toHaveBeenCalled(); + const callArgs = openSpy.mock.calls[0]; + expect(callArgs[1]).toEqual({ + data: { + policy: mockPolicy, + organizationId: mockOrgId, + }, + }); + }); + + it("should call custom dialog open method when specified", () => { + const mockDialogRef = { close: jest.fn() }; + const mockCustomDialog = { + open: jest.fn().mockReturnValue(mockDialogRef), + }; + + const mockPolicy: BasePolicyEditDefinition = { + name: "Custom Policy", + description: "Custom Description", + type: PolicyType.RequireSso, + component: {} as any, + editDialogComponent: mockCustomDialog as any, + showDescription: true, + display$: () => of(true), + }; + + component.edit(mockPolicy, mockOrgId); + + expect(mockCustomDialog.open).toHaveBeenCalled(); + const callArgs = mockCustomDialog.open.mock.calls[0]; + expect(callArgs[1]).toEqual({ + data: { + policy: mockPolicy, + organizationId: mockOrgId, + }, + }); + expect(PolicyEditDialogComponent.open).not.toHaveBeenCalled(); + }); + + it("should pass correct organizationId to dialog", () => { + const customOrgId = newGuid() as OrganizationId; + const mockPolicy: BasePolicyEditDefinition = { + name: "Test Policy", + description: "Test Description", + type: PolicyType.SingleOrg, + component: {} as any, + showDescription: true, + display$: () => of(true), + }; + + const openSpy = jest.spyOn(PolicyEditDialogComponent, "open"); + + component.edit(mockPolicy, customOrgId); + + expect(openSpy).toHaveBeenCalled(); + const callArgs = openSpy.mock.calls[0]; + expect(callArgs[1]).toEqual({ + data: { + policy: mockPolicy, + organizationId: customOrgId, + }, + }); + }); + }); +}); diff --git a/apps/web/src/app/admin-console/organizations/policies/policies.component.ts b/apps/web/src/app/admin-console/organizations/policies/policies.component.ts index e80796fd0af..70daf55f662 100644 --- a/apps/web/src/app/admin-console/organizations/policies/policies.component.ts +++ b/apps/web/src/app/admin-console/organizations/policies/policies.component.ts @@ -1,31 +1,19 @@ -// FIXME: Update this file to be type safe and remove this and next line -// @ts-strict-ignore -import { Component, OnInit } from "@angular/core"; +import { ChangeDetectionStrategy, Component, DestroyRef } from "@angular/core"; import { takeUntilDestroyed } from "@angular/core/rxjs-interop"; import { ActivatedRoute } from "@angular/router"; -import { - combineLatest, - firstValueFrom, - Observable, - of, - switchMap, - first, - map, - withLatestFrom, - tap, -} from "rxjs"; +import { combineLatest, Observable, of, switchMap, first, map } from "rxjs"; -import { - getOrganizationById, - OrganizationService, -} from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; +import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; import { PolicyApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/policy/policy-api.service.abstraction"; import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; import { PolicyType } from "@bitwarden/common/admin-console/enums"; +import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; import { PolicyResponse } from "@bitwarden/common/admin-console/models/response/policy.response"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { getUserId } from "@bitwarden/common/auth/services/account.service"; import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; +import { getById } from "@bitwarden/common/platform/misc"; +import { OrganizationId, UserId } from "@bitwarden/common/types/guid"; import { DialogService } from "@bitwarden/components"; import { safeProvider } from "@bitwarden/ui-common"; @@ -37,8 +25,6 @@ import { PolicyEditDialogComponent } from "./policy-edit-dialog.component"; import { PolicyListService } from "./policy-list.service"; import { POLICY_EDIT_REGISTER } from "./policy-register-token"; -// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush -// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection @Component({ templateUrl: "policies.component.html", imports: [SharedModule, HeaderModule], @@ -48,14 +34,53 @@ import { POLICY_EDIT_REGISTER } from "./policy-register-token"; deps: [POLICY_EDIT_REGISTER], }), ], + changeDetection: ChangeDetectionStrategy.OnPush, }) -export class PoliciesComponent implements OnInit { - loading = true; - organizationId: string; - policies$: Observable; +export class PoliciesComponent { + private userId$: Observable = this.accountService.activeAccount$.pipe(getUserId); - private orgPolicies: PolicyResponse[]; - protected policiesEnabledMap: Map = new Map(); + protected organizationId$: Observable = this.route.params.pipe( + map((params) => params.organizationId), + ); + + protected organization$: Observable = combineLatest([ + this.userId$, + this.organizationId$, + ]).pipe( + switchMap(([userId, orgId]) => + this.organizationService.organizations$(userId).pipe( + getById(orgId), + map((org) => { + if (org == null) { + throw new Error("No organization found for provided userId"); + } + return org; + }), + ), + ), + ); + + protected policies$: Observable = of( + this.policyListService.getPolicies(), + ); + + private orgPolicies$: Observable = this.accountService.activeAccount$.pipe( + getUserId, + switchMap((userId) => this.policyService.policies$(userId)), + switchMap(() => this.organizationId$), + switchMap((organizationId) => this.policyApiService.getPolicies(organizationId)), + map((response) => (response.data != null && response.data.length > 0 ? response.data : [])), + ); + + protected policiesEnabledMap$: Observable> = this.orgPolicies$.pipe( + map((orgPolicies) => { + const policiesEnabledMap: Map = new Map(); + orgPolicies.forEach((op) => { + policiesEnabledMap.set(op.type, op.enabled); + }); + return policiesEnabledMap; + }), + ); constructor( private route: ActivatedRoute, @@ -66,60 +91,28 @@ export class PoliciesComponent implements OnInit { private dialogService: DialogService, private policyService: PolicyService, protected configService: ConfigService, + private destroyRef: DestroyRef, ) { - this.accountService.activeAccount$ - .pipe( - getUserId, - switchMap((userId) => this.policyService.policies$(userId)), - tap(async () => await this.load()), - takeUntilDestroyed(), - ) - .subscribe(); + this.handleLaunchEvent(); } - async ngOnInit() { - // eslint-disable-next-line rxjs-angular/prefer-takeuntil, rxjs/no-async-subscribe - this.route.parent.parent.params.subscribe(async (params) => { - this.organizationId = params.organizationId; - const userId = await firstValueFrom( - this.accountService.activeAccount$.pipe(map((a) => a?.id)), - ); - - const organization$ = this.organizationService - .organizations$(userId) - .pipe(getOrganizationById(this.organizationId)); - - this.policies$ = organization$.pipe( - withLatestFrom(of(this.policyListService.getPolicies())), - switchMap(([organization, policies]) => { - return combineLatest( - policies.map((policy) => - policy - .display$(organization, this.configService) - .pipe(map((shouldDisplay) => ({ policy, shouldDisplay }))), - ), - ); - }), - map((results) => - results.filter((result) => result.shouldDisplay).map((result) => result.policy), - ), - ); - - await this.load(); - - // Handle policies component launch from Event message - combineLatest([this.route.queryParams.pipe(first()), this.policies$]) - /* eslint-disable-next-line rxjs-angular/prefer-takeuntil, rxjs/no-async-subscribe, rxjs/no-nested-subscribe */ - .subscribe(async ([qParams, policies]) => { + // Handle policies component launch from Event message + private handleLaunchEvent() { + combineLatest([ + this.route.queryParams.pipe(first()), + this.policies$, + this.organizationId$, + this.orgPolicies$, + ]) + .pipe( + map(([qParams, policies, organizationId, orgPolicies]) => { if (qParams.policyId != null) { const policyIdFromEvents: string = qParams.policyId; - for (const orgPolicy of this.orgPolicies) { + for (const orgPolicy of orgPolicies) { if (orgPolicy.id === policyIdFromEvents) { for (let i = 0; i < policies.length; i++) { if (policies[i].type === orgPolicy.type) { - // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. - // eslint-disable-next-line @typescript-eslint/no-floating-promises - this.edit(policies[i]); + this.edit(policies[i], organizationId); break; } } @@ -127,27 +120,19 @@ export class PoliciesComponent implements OnInit { } } } - }); - }); + }), + takeUntilDestroyed(this.destroyRef), + ) + .subscribe(); } - async load() { - const response = await this.policyApiService.getPolicies(this.organizationId); - this.orgPolicies = response.data != null && response.data.length > 0 ? response.data : []; - this.orgPolicies.forEach((op) => { - this.policiesEnabledMap.set(op.type, op.enabled); - }); - - this.loading = false; - } - - async edit(policy: BasePolicyEditDefinition) { + edit(policy: BasePolicyEditDefinition, organizationId: OrganizationId) { const dialogComponent: PolicyDialogComponent = policy.editDialogComponent ?? PolicyEditDialogComponent; dialogComponent.open(this.dialogService, { data: { policy: policy, - organizationId: this.organizationId, + organizationId: organizationId, }, }); } diff --git a/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/auto-confirm-policy.component.ts b/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/auto-confirm-policy.component.ts index 7fa4fc2eea7..cf2a2929905 100644 --- a/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/auto-confirm-policy.component.ts +++ b/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/auto-confirm-policy.component.ts @@ -1,4 +1,11 @@ -import { Component, OnInit, Signal, TemplateRef, viewChild } from "@angular/core"; +import { + ChangeDetectionStrategy, + Component, + OnInit, + Signal, + TemplateRef, + viewChild, +} from "@angular/core"; import { BehaviorSubject, map, Observable } from "rxjs"; import { AutoConfirmSvg } from "@bitwarden/assets/svg"; @@ -26,11 +33,11 @@ export class AutoConfirmPolicy extends BasePolicyEditDefinition { } } -// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush -// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection @Component({ + selector: "auto-confirm-policy-edit", templateUrl: "auto-confirm-policy.component.html", imports: [SharedModule], + changeDetection: ChangeDetectionStrategy.OnPush, }) export class AutoConfirmPolicyEditComponent extends BasePolicyEditComponent implements OnInit { protected readonly autoConfirmSvg = AutoConfirmSvg; diff --git a/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/autotype-policy.component.ts b/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/autotype-policy.component.ts index ceace60cd99..809ffd39a64 100644 --- a/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/autotype-policy.component.ts +++ b/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/autotype-policy.component.ts @@ -1,4 +1,4 @@ -import { Component } from "@angular/core"; +import { ChangeDetectionStrategy, Component } from "@angular/core"; import { PolicyType } from "@bitwarden/common/admin-console/enums"; import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; @@ -18,10 +18,10 @@ export class DesktopAutotypeDefaultSettingPolicy extends BasePolicyEditDefinitio return configService.getFeatureFlag$(FeatureFlag.WindowsDesktopAutotype); } } -// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush -// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection @Component({ + selector: "autotype-policy-edit", templateUrl: "autotype-policy.component.html", imports: [SharedModule], + changeDetection: ChangeDetectionStrategy.OnPush, }) export class DesktopAutotypeDefaultSettingPolicyComponent extends BasePolicyEditComponent {} diff --git a/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/disable-send.component.ts b/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/disable-send.component.ts index 103420fbf51..d5d4a207598 100644 --- a/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/disable-send.component.ts +++ b/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/disable-send.component.ts @@ -1,4 +1,4 @@ -import { Component } from "@angular/core"; +import { ChangeDetectionStrategy, Component } from "@angular/core"; import { PolicyType } from "@bitwarden/common/admin-console/enums"; @@ -12,10 +12,10 @@ export class DisableSendPolicy extends BasePolicyEditDefinition { component = DisableSendPolicyComponent; } -// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush -// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection @Component({ + selector: "disable-send-policy-edit", templateUrl: "disable-send.component.html", imports: [SharedModule], + changeDetection: ChangeDetectionStrategy.OnPush, }) export class DisableSendPolicyComponent extends BasePolicyEditComponent {} diff --git a/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/master-password.component.ts b/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/master-password.component.ts index c1223a2004b..e9926b2aeb1 100644 --- a/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/master-password.component.ts +++ b/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/master-password.component.ts @@ -1,6 +1,6 @@ // FIXME: Update this file to be type safe and remove this and next line // @ts-strict-ignore -import { Component, OnInit } from "@angular/core"; +import { ChangeDetectionStrategy, Component, OnInit } from "@angular/core"; import { FormBuilder, FormGroup, Validators } from "@angular/forms"; import { firstValueFrom } from "rxjs"; @@ -26,11 +26,11 @@ export class MasterPasswordPolicy extends BasePolicyEditDefinition { component = MasterPasswordPolicyComponent; } -// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush -// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection @Component({ + selector: "master-password-policy-edit", templateUrl: "master-password.component.html", imports: [SharedModule], + changeDetection: ChangeDetectionStrategy.OnPush, }) export class MasterPasswordPolicyComponent extends BasePolicyEditComponent implements OnInit { MinPasswordLength = Utils.minimumPasswordLength; diff --git a/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/organization-data-ownership.component.ts b/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/organization-data-ownership.component.ts index d832dff158a..ec857478a21 100644 --- a/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/organization-data-ownership.component.ts +++ b/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/organization-data-ownership.component.ts @@ -1,4 +1,4 @@ -import { Component } from "@angular/core"; +import { ChangeDetectionStrategy, Component } from "@angular/core"; import { map, Observable } from "rxjs"; import { PolicyType } from "@bitwarden/common/admin-console/enums"; @@ -22,10 +22,10 @@ export class OrganizationDataOwnershipPolicy extends BasePolicyEditDefinition { } } -// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush -// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection @Component({ + selector: "organization-data-ownership-policy-edit", templateUrl: "organization-data-ownership.component.html", imports: [SharedModule], + changeDetection: ChangeDetectionStrategy.OnPush, }) export class OrganizationDataOwnershipPolicyComponent extends BasePolicyEditComponent {} diff --git a/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/password-generator.component.ts b/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/password-generator.component.ts index e3a67362cc9..b338f3ec508 100644 --- a/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/password-generator.component.ts +++ b/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/password-generator.component.ts @@ -1,6 +1,6 @@ // FIXME: Update this file to be type safe and remove this and next line // @ts-strict-ignore -import { Component } from "@angular/core"; +import { ChangeDetectionStrategy, Component } from "@angular/core"; import { takeUntilDestroyed } from "@angular/core/rxjs-interop"; import { UntypedFormBuilder, Validators } from "@angular/forms"; import { BehaviorSubject, map } from "rxjs"; @@ -19,11 +19,11 @@ export class PasswordGeneratorPolicy extends BasePolicyEditDefinition { component = PasswordGeneratorPolicyComponent; } -// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush -// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection @Component({ + selector: "password-generator-policy-edit", templateUrl: "password-generator.component.html", imports: [SharedModule], + changeDetection: ChangeDetectionStrategy.OnPush, }) export class PasswordGeneratorPolicyComponent extends BasePolicyEditComponent { // these properties forward the application default settings to the UI diff --git a/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/remove-unlock-with-pin.component.ts b/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/remove-unlock-with-pin.component.ts index ac768d47d6e..443a1792956 100644 --- a/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/remove-unlock-with-pin.component.ts +++ b/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/remove-unlock-with-pin.component.ts @@ -1,4 +1,4 @@ -import { Component } from "@angular/core"; +import { ChangeDetectionStrategy, Component } from "@angular/core"; import { PolicyType } from "@bitwarden/common/admin-console/enums"; @@ -12,10 +12,10 @@ export class RemoveUnlockWithPinPolicy extends BasePolicyEditDefinition { component = RemoveUnlockWithPinPolicyComponent; } -// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush -// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection @Component({ + selector: "remove-unlock-with-pin-policy-edit", templateUrl: "remove-unlock-with-pin.component.html", imports: [SharedModule], + changeDetection: ChangeDetectionStrategy.OnPush, }) export class RemoveUnlockWithPinPolicyComponent extends BasePolicyEditComponent {} diff --git a/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/require-sso.component.ts b/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/require-sso.component.ts index 904c29ca70d..e7cded03f11 100644 --- a/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/require-sso.component.ts +++ b/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/require-sso.component.ts @@ -1,4 +1,4 @@ -import { Component } from "@angular/core"; +import { ChangeDetectionStrategy, Component } from "@angular/core"; import { of } from "rxjs"; import { PolicyType } from "@bitwarden/common/admin-console/enums"; @@ -19,10 +19,10 @@ export class RequireSsoPolicy extends BasePolicyEditDefinition { } } -// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush -// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection @Component({ + selector: "require-sso-policy-edit", templateUrl: "require-sso.component.html", imports: [SharedModule], + changeDetection: ChangeDetectionStrategy.OnPush, }) export class RequireSsoPolicyComponent extends BasePolicyEditComponent {} diff --git a/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/reset-password.component.ts b/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/reset-password.component.ts index bfe149048e3..4b194075aef 100644 --- a/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/reset-password.component.ts +++ b/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/reset-password.component.ts @@ -1,4 +1,4 @@ -import { Component, OnInit } from "@angular/core"; +import { ChangeDetectionStrategy, Component, OnInit } from "@angular/core"; import { FormBuilder } from "@angular/forms"; import { firstValueFrom, of } from "rxjs"; @@ -26,11 +26,11 @@ export class ResetPasswordPolicy extends BasePolicyEditDefinition { } } -// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush -// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection @Component({ + selector: "reset-password-policy-edit", templateUrl: "reset-password.component.html", imports: [SharedModule], + changeDetection: ChangeDetectionStrategy.OnPush, }) export class ResetPasswordPolicyComponent extends BasePolicyEditComponent implements OnInit { data = this.formBuilder.group({ diff --git a/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/restricted-item-types.component.ts b/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/restricted-item-types.component.ts index 554542f8a84..279b40ef5ee 100644 --- a/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/restricted-item-types.component.ts +++ b/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/restricted-item-types.component.ts @@ -1,4 +1,4 @@ -import { Component } from "@angular/core"; +import { ChangeDetectionStrategy, Component } from "@angular/core"; import { PolicyType } from "@bitwarden/common/admin-console/enums"; @@ -12,11 +12,11 @@ export class RestrictedItemTypesPolicy extends BasePolicyEditDefinition { component = RestrictedItemTypesPolicyComponent; } -// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush -// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection @Component({ + selector: "restricted-item-types-policy-edit", templateUrl: "restricted-item-types.component.html", imports: [SharedModule], + changeDetection: ChangeDetectionStrategy.OnPush, }) export class RestrictedItemTypesPolicyComponent extends BasePolicyEditComponent { constructor() { diff --git a/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/send-options.component.ts b/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/send-options.component.ts index b8a59e8f8ef..06fd672fa4e 100644 --- a/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/send-options.component.ts +++ b/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/send-options.component.ts @@ -1,4 +1,4 @@ -import { Component } from "@angular/core"; +import { ChangeDetectionStrategy, Component } from "@angular/core"; import { UntypedFormBuilder } from "@angular/forms"; import { PolicyType } from "@bitwarden/common/admin-console/enums"; @@ -13,11 +13,11 @@ export class SendOptionsPolicy extends BasePolicyEditDefinition { component = SendOptionsPolicyComponent; } -// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush -// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection @Component({ + selector: "send-options-policy-edit", templateUrl: "send-options.component.html", imports: [SharedModule], + changeDetection: ChangeDetectionStrategy.OnPush, }) export class SendOptionsPolicyComponent extends BasePolicyEditComponent { data = this.formBuilder.group({ diff --git a/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/single-org.component.ts b/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/single-org.component.ts index 655c5f20610..dfdb4d4ddaf 100644 --- a/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/single-org.component.ts +++ b/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/single-org.component.ts @@ -1,4 +1,4 @@ -import { Component, OnInit } from "@angular/core"; +import { ChangeDetectionStrategy, Component, OnInit } from "@angular/core"; import { PolicyType } from "@bitwarden/common/admin-console/enums"; @@ -12,11 +12,11 @@ export class SingleOrgPolicy extends BasePolicyEditDefinition { component = SingleOrgPolicyComponent; } -// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush -// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection @Component({ + selector: "single-org-policy-edit", templateUrl: "single-org.component.html", imports: [SharedModule], + changeDetection: ChangeDetectionStrategy.OnPush, }) export class SingleOrgPolicyComponent extends BasePolicyEditComponent implements OnInit { async ngOnInit() { diff --git a/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/two-factor-authentication.component.ts b/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/two-factor-authentication.component.ts index 62f3d1f3466..8700714f6f4 100644 --- a/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/two-factor-authentication.component.ts +++ b/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/two-factor-authentication.component.ts @@ -1,4 +1,4 @@ -import { Component } from "@angular/core"; +import { ChangeDetectionStrategy, Component } from "@angular/core"; import { PolicyType } from "@bitwarden/common/admin-console/enums"; @@ -12,10 +12,10 @@ export class TwoFactorAuthenticationPolicy extends BasePolicyEditDefinition { component = TwoFactorAuthenticationPolicyComponent; } -// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush -// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection @Component({ + selector: "two-factor-authentication-policy-edit", templateUrl: "two-factor-authentication.component.html", imports: [SharedModule], + changeDetection: ChangeDetectionStrategy.OnPush, }) export class TwoFactorAuthenticationPolicyComponent extends BasePolicyEditComponent {} diff --git a/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/uri-match-default.component.ts b/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/uri-match-default.component.ts index 5c0b667bea2..d88b1d0769a 100644 --- a/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/uri-match-default.component.ts +++ b/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/uri-match-default.component.ts @@ -19,6 +19,7 @@ export class UriMatchDefaultPolicy extends BasePolicyEditDefinition { component = UriMatchDefaultPolicyComponent; } @Component({ + selector: "uri-match-default-policy-edit", changeDetection: ChangeDetectionStrategy.OnPush, templateUrl: "uri-match-default.component.html", imports: [SharedModule], diff --git a/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/vnext-organization-data-ownership.component.ts b/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/vnext-organization-data-ownership.component.ts index a0d425d5886..ed26dd37801 100644 --- a/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/vnext-organization-data-ownership.component.ts +++ b/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/vnext-organization-data-ownership.component.ts @@ -1,4 +1,4 @@ -import { Component, OnInit, TemplateRef, ViewChild } from "@angular/core"; +import { ChangeDetectionStrategy, Component, OnInit, TemplateRef, ViewChild } from "@angular/core"; import { lastValueFrom, Observable } from "rxjs"; import { PolicyType } from "@bitwarden/common/admin-console/enums"; @@ -34,11 +34,11 @@ export class vNextOrganizationDataOwnershipPolicy extends BasePolicyEditDefiniti } } -// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush -// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection @Component({ + selector: "vnext-organization-data-ownership-policy-edit", templateUrl: "vnext-organization-data-ownership.component.html", imports: [SharedModule], + changeDetection: ChangeDetectionStrategy.OnPush, }) export class vNextOrganizationDataOwnershipPolicyComponent extends BasePolicyEditComponent diff --git a/bitwarden_license/bit-web/src/app/admin-console/policies/policy-edit-definitions/activate-autofill.component.ts b/bitwarden_license/bit-web/src/app/admin-console/policies/policy-edit-definitions/activate-autofill.component.ts index c32eb3d935b..08fe807f669 100644 --- a/bitwarden_license/bit-web/src/app/admin-console/policies/policy-edit-definitions/activate-autofill.component.ts +++ b/bitwarden_license/bit-web/src/app/admin-console/policies/policy-edit-definitions/activate-autofill.component.ts @@ -24,6 +24,7 @@ export class ActivateAutofillPolicy extends BasePolicyEditDefinition { // FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush // eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection @Component({ + selector: "activate-autofill-policy-edit", templateUrl: "activate-autofill.component.html", imports: [SharedModule], }) diff --git a/bitwarden_license/bit-web/src/app/admin-console/policies/policy-edit-definitions/automatic-app-login.component.ts b/bitwarden_license/bit-web/src/app/admin-console/policies/policy-edit-definitions/automatic-app-login.component.ts index eb82dce4383..23603490d50 100644 --- a/bitwarden_license/bit-web/src/app/admin-console/policies/policy-edit-definitions/automatic-app-login.component.ts +++ b/bitwarden_license/bit-web/src/app/admin-console/policies/policy-edit-definitions/automatic-app-login.component.ts @@ -20,6 +20,7 @@ export class AutomaticAppLoginPolicy extends BasePolicyEditDefinition { // FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush // eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection @Component({ + selector: "automatic-app-login-policy-edit", templateUrl: "automatic-app-login.component.html", imports: [SharedModule], }) diff --git a/bitwarden_license/bit-web/src/app/admin-console/policies/policy-edit-definitions/block-claimed-domain-account-creation.component.ts b/bitwarden_license/bit-web/src/app/admin-console/policies/policy-edit-definitions/block-claimed-domain-account-creation.component.ts index 5e2925aa0bb..75c61e3e7e3 100644 --- a/bitwarden_license/bit-web/src/app/admin-console/policies/policy-edit-definitions/block-claimed-domain-account-creation.component.ts +++ b/bitwarden_license/bit-web/src/app/admin-console/policies/policy-edit-definitions/block-claimed-domain-account-creation.component.ts @@ -25,6 +25,7 @@ export class BlockClaimedDomainAccountCreationPolicy extends BasePolicyEditDefin } @Component({ + selector: "block-claimed-domain-account-creation-policy-edit", changeDetection: ChangeDetectionStrategy.OnPush, templateUrl: "block-claimed-domain-account-creation.component.html", imports: [SharedModule], diff --git a/bitwarden_license/bit-web/src/app/admin-console/policies/policy-edit-definitions/disable-personal-vault-export.component.ts b/bitwarden_license/bit-web/src/app/admin-console/policies/policy-edit-definitions/disable-personal-vault-export.component.ts index 17e8eb055b5..0f0fc5f358d 100644 --- a/bitwarden_license/bit-web/src/app/admin-console/policies/policy-edit-definitions/disable-personal-vault-export.component.ts +++ b/bitwarden_license/bit-web/src/app/admin-console/policies/policy-edit-definitions/disable-personal-vault-export.component.ts @@ -17,6 +17,7 @@ export class DisablePersonalVaultExportPolicy extends BasePolicyEditDefinition { // FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush // eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection @Component({ + selector: "disable-personal-vault-export-policy-edit", templateUrl: "disable-personal-vault-export.component.html", imports: [SharedModule], })