From 18c1d8b2d321419d927975c119ad38e780f46a0b Mon Sep 17 00:00:00 2001 From: Jordan Aasen <166539328+jaasen-livefront@users.noreply.github.com> Date: Thu, 13 Nov 2025 10:34:38 -0800 Subject: [PATCH] [PM-27661] - Multiple URIs - Add a Collapse Button after clicking View All for Saved Websites (#17352) * use signals. add toggleable list view. * use @for. remove redundant if statement * fix template variable name * clean up test setup * Update apps/browser/src/vault/popup/components/vault-v2/autofill-confirmation-dialog/autofill-confirmation-dialog.component.spec.ts Co-authored-by: Nik Gilmore --------- Co-authored-by: Nik Gilmore --- apps/browser/src/_locales/en/messages.json | 3 + ...utofill-confirmation-dialog.component.html | 44 +++++------ ...fill-confirmation-dialog.component.spec.ts | 75 ++++++++----------- .../autofill-confirmation-dialog.component.ts | 56 ++++++-------- .../item-more-options.component.html | 4 +- .../item-more-options.component.spec.ts | 21 ++---- .../item-more-options.component.ts | 9 +-- 7 files changed, 94 insertions(+), 118 deletions(-) diff --git a/apps/browser/src/_locales/en/messages.json b/apps/browser/src/_locales/en/messages.json index 0ff2db480c..2384cf8c4e 100644 --- a/apps/browser/src/_locales/en/messages.json +++ b/apps/browser/src/_locales/en/messages.json @@ -594,6 +594,9 @@ "viewAll": { "message": "View all" }, + "viewLess": { + "message": "View less" + }, "viewLogin": { "message": "View login" }, diff --git a/apps/browser/src/vault/popup/components/vault-v2/autofill-confirmation-dialog/autofill-confirmation-dialog.component.html b/apps/browser/src/vault/popup/components/vault-v2/autofill-confirmation-dialog/autofill-confirmation-dialog.component.html index 39ec6bc28a..6f61c5fa44 100644 --- a/apps/browser/src/vault/popup/components/vault-v2/autofill-confirmation-dialog/autofill-confirmation-dialog.component.html +++ b/apps/browser/src/vault/popup/components/vault-v2/autofill-confirmation-dialog/autofill-confirmation-dialog.component.html @@ -1,60 +1,60 @@ - - {{ "confirmAutofill" | i18n }} +

{{ "confirmAutofillDesc" | i18n }}

- @if (savedUrls.length === 1) { + @if (savedUrls().length === 1) {

{{ "savedWebsite" | i18n }}

-
- {{ savedUrls[0] }} +
+ {{ savedUrls()[0] }}
} - @if (savedUrls.length > 1) { + @if (savedUrls().length > 1) {

- {{ "savedWebsites" | i18n: savedUrls.length }} + {{ "savedWebsites" | i18n: savedUrls().length }}

-
-
- -
- {{ url }} -
-
-
+
+ @for (url of savedUrls(); track url) { +
+ +
+ {{ url }} +
+
+
+ }
}

{{ "currentWebsite" | i18n }}

-
- {{ currentUrl }} +
+ {{ currentUrl() }}
- @if (!viewOnly) { + @if (!viewOnly()) { } - @if (!(showAutofillConfirmation$ | async)) { + @if (!(autofillConfirmationFlagEnabled$ | async)) { } diff --git a/apps/browser/src/vault/popup/components/vault-v2/item-more-options/item-more-options.component.spec.ts b/apps/browser/src/vault/popup/components/vault-v2/item-more-options/item-more-options.component.spec.ts index 5927da6c3d..7b71c2b470 100644 --- a/apps/browser/src/vault/popup/components/vault-v2/item-more-options/item-more-options.component.spec.ts +++ b/apps/browser/src/vault/popup/components/vault-v2/item-more-options/item-more-options.component.spec.ts @@ -2,6 +2,7 @@ import { CUSTOM_ELEMENTS_SCHEMA } from "@angular/core"; import { ComponentFixture, TestBed, waitForAsync } from "@angular/core/testing"; import { NoopAnimationsModule } from "@angular/platform-browser/animations"; import { Router } from "@angular/router"; +import { mock } from "jest-mock-extended"; import { BehaviorSubject, of } from "rxjs"; import { CollectionService } from "@bitwarden/admin-console/common"; @@ -66,11 +67,6 @@ describe("ItemMoreOptionsComponent", () => { resolvedDefaultUriMatchStrategy$: uriMatchStrategy$.asObservable(), }; - const hasSearchText$ = new BehaviorSubject(false); - const vaultPopupItemsService = { - hasSearchText$: hasSearchText$.asObservable(), - }; - const baseCipher = { id: "cipher-1", login: { @@ -120,7 +116,7 @@ describe("ItemMoreOptionsComponent", () => { }, { provide: VaultPopupItemsService, - useValue: vaultPopupItemsService, + useValue: mock({}), }, ], schemas: [CUSTOM_ELEMENTS_SCHEMA], @@ -153,7 +149,7 @@ describe("ItemMoreOptionsComponent", () => { expect(passwordRepromptService.passwordRepromptCheck).toHaveBeenCalledWith(baseCipher); }); - it("calls the autofill service to autofill without showing the confirmation dialog when the feature flag is disabled or search text is not present", async () => { + it("calls the autofill service to autofill without showing the confirmation dialog when the feature flag is disabled", async () => { autofillSvc.currentAutofillTab$.next({ url: "https://page.example.com" }); await component.doAutofill(); @@ -182,7 +178,7 @@ describe("ItemMoreOptionsComponent", () => { }); it("does not show the exact match dialog when the default match strategy is Exact and autofill confirmation is not to be shown", async () => { - // autofill confirmation dialog is not shown when either the feature flag is disabled or search text is not present + // autofill confirmation dialog is not shown when either the feature flag is disabled uriMatchStrategy$.next(UriMatchStrategy.Exact); autofillSvc.currentAutofillTab$.next({ url: "https://page.example.com/path" }); await component.doAutofill(); @@ -192,9 +188,8 @@ describe("ItemMoreOptionsComponent", () => { describe("autofill confirmation dialog", () => { beforeEach(() => { - // autofill confirmation dialog is shown when feature flag is enabled and search text is present + // autofill confirmation dialog is shown when feature flag is enabled featureFlag$.next(true); - hasSearchText$.next(true); uriMatchStrategy$.next(UriMatchStrategy.Domain); passwordRepromptService.passwordRepromptCheck.mockResolvedValue(true); }); @@ -208,7 +203,7 @@ describe("ItemMoreOptionsComponent", () => { expect(passwordRepromptService.passwordRepromptCheck).toHaveBeenCalledWith(baseCipher); }); - it("opens the autofill confirmation dialog with filtered saved URLs when the feature flag is enabled and search text is present", async () => { + it("opens the autofill confirmation dialog with filtered saved URLs when the feature flag is enabled", async () => { autofillSvc.currentAutofillTab$.next({ url: "https://page.example.com/path" }); const openSpy = mockConfirmDialogResult(AutofillConfirmationDialogResult.Canceled); @@ -216,8 +211,8 @@ describe("ItemMoreOptionsComponent", () => { expect(openSpy).toHaveBeenCalledTimes(1); const args = openSpy.mock.calls[0][1]; - expect(args.data.currentUrl).toBe("https://page.example.com/path"); - expect(args.data.savedUrls).toEqual([ + expect(args.data?.currentUrl).toBe("https://page.example.com/path"); + expect(args.data?.savedUrls).toEqual([ "https://one.example.com", "https://two.example.com/a", ]); diff --git a/apps/browser/src/vault/popup/components/vault-v2/item-more-options/item-more-options.component.ts b/apps/browser/src/vault/popup/components/vault-v2/item-more-options/item-more-options.component.ts index 1316a0d32b..b498e7cd9a 100644 --- a/apps/browser/src/vault/popup/components/vault-v2/item-more-options/item-more-options.component.ts +++ b/apps/browser/src/vault/popup/components/vault-v2/item-more-options/item-more-options.component.ts @@ -84,10 +84,9 @@ export class ItemMoreOptionsComponent { protected autofillAllowed$ = this.vaultPopupAutofillService.autofillAllowed$; - protected showAutofillConfirmation$ = combineLatest([ - this.configService.getFeatureFlag$(FeatureFlag.AutofillConfirmation), - this.vaultPopupItemsService.hasSearchText$, - ]).pipe(map(([isFeatureFlagEnabled, hasSearchText]) => isFeatureFlagEnabled && hasSearchText)); + protected autofillConfirmationFlagEnabled$ = this.configService + .getFeatureFlag$(FeatureFlag.AutofillConfirmation) + .pipe(map((isFeatureFlagEnabled) => isFeatureFlagEnabled)); protected uriMatchStrategy$ = this.domainSettingsService.resolvedDefaultUriMatchStrategy$; @@ -210,7 +209,7 @@ export class ItemMoreOptionsComponent { const cipherHasAllExactMatchLoginUris = uris.length > 0 && uris.every((u) => u.uri && u.match === UriMatchStrategy.Exact); - const showAutofillConfirmation = await firstValueFrom(this.showAutofillConfirmation$); + const showAutofillConfirmation = await firstValueFrom(this.autofillConfirmationFlagEnabled$); const uriMatchStrategy = await firstValueFrom(this.uriMatchStrategy$); if (