Repository: bitwarden/clients
Pull request #14342: PM-20396 open view item vault pop out
Author: login: dan-livefront
Labels: name: enhancement
Created: 2025-04-18T18:09:07Z | Merged: 2025-04-23T18:47:10Z

## 🎟️ Tracking

<!-- Paste the link to the Jira or GitHub issue or otherwise describe / point to where this change is coming from. -->
https://bitwarden.atlassian.net/browse/PM-20396
## 📔 Objective

<!-- Describe what the purpose of this PR is, for example what bug you're fixing or new feature you're adding. -->
Design has requested we change the username to the uri name pertaining to a cipher that has either been edited or updated. Additionally, we want to open the view page not the edit page as we were erroneously doing before.  Added aria label for SR reader as well. 
## 📸 Screenshots

<!-- Required for any UI changes; delete if not applicable. Use fixed width images for better display. -->
![Screenshot 2025-04-22 at 10 24 11 AM](https://github.com/user-attachments/assets/43f016e3-b158-4b98-b055-0a3062875d1b)
![Screenshot 2025-04-22 at 10 24 21 AM](https://github.com/user-attachments/assets/db741c2d-756b-4ba7-aed0-93b9978d6cd4)

## ⏰ Reminders before review

- Contributor guidelines followed
- All formatters and local linters executed and passed
- Written new unit and / or integration tests where applicable
- Protected functional changes with optionality (feature flags)
- Used internationalization (i18n) for all UI strings
- CI builds passed
- Communicated to DevOps any deployment requirements
- Updated any necessary documentation (Confluence, contributing docs) or informed the documentation team

## 🦮 Reviewer guidelines

<!-- Suggested interactions but feel free to use (or not) as you desire! -->

- 👍 (`:+1:`) or similar for great changes
- 📝 (`:memo:`) or ℹ️ (`:information_source:`) for notes or general info
- ❓ (`:question:`) for questions
- 🤔 (`:thinking:`) or 💭 (`:thought_balloon:`) for more open inquiry that's not quite a confirmed issue and could potentially benefit from discussion
- 🎨 (`:art:`) for suggestions / improvements
- ❌ (`:x:`) or ⚠️ (`:warning:`) for more significant problems or concerns needing attention
- 🌱 (`:seedling:`) or ♻️ (`:recycle:`) for future improvements or indications of technical debt
- ⛏ (`:pick:`) for minor or nitpick changes



Diff:
diff --git a/apps/browser/src/_locales/en/messages.json b/apps/browser/src/_locales/en/messages.json
index 73c64ba7fd4d..e8561424777a 100644
--- a/apps/browser/src/_locales/en/messages.json
+++ b/apps/browser/src/_locales/en/messages.json
@@ -192,7 +192,7 @@
     "message": "Copy",
     "description": "Copy to clipboard"
   },
-  "fill":{
+  "fill": {
     "message": "Fill",
     "description": "This string is used on the vault page to indicate autofilling. Horizontal space is limited in the interface here so try and keep translations as concise as possible."
   },
@@ -1059,6 +1059,15 @@
   "notificationAddSave": {
     "message": "Save"
   },
+  "notificationViewAria": {
+    "message": "View $ITEMNAME$, opens in new window",
+    "placeholders": {
+      "itemName": {
+        "content": "$1"
+      }
+    },
+    "description": "Aria label for the view button in notification bar confirmation message"
+  },
   "newNotification": {
     "message": "New notification"
   },
@@ -1072,23 +1081,23 @@
       }
     }
   },
-  "loginSaveSuccessDetails": {
-    "message": "$USERNAME$ saved to Bitwarden.",
-      "placeholders": {
-        "username": {
-          "content": "$1"
-        }
-      },
-      "description": "Shown to user after login is saved."
+  "loginSaveConfirmation": {
+    "message": "$ITEMNAME$ saved to Bitwarden.",
+    "placeholders": {
+      "itemName": {
+        "content": "$1"
+      }
+    },
+    "description": "Shown to user after item is saved."
   },
-  "loginUpdatedSuccessDetails": {
-    "message": "$USERNAME$ updated in Bitwarden.",
-      "placeholders": {
-        "username": {
-          "content": "$1"
-        }
-      },
-      "description": "Shown to user after login is updated."
+  "loginUpdatedConfirmation": {
+    "message": "$ITEMNAME$ updated in Bitwarden.",
+    "placeholders": {
+      "itemName": {
+        "content": "$1"
+      }
+    },
+    "description": "Shown to user after item is updated."
   },
   "saveAsNewLoginAction": {
     "message": "Save as new login",
diff --git a/apps/browser/src/autofill/background/abstractions/notification.background.ts b/apps/browser/src/autofill/background/abstractions/notification.background.ts
index 6b3c91a109c2..c93fd9a3acf5 100644
--- a/apps/browser/src/autofill/background/abstractions/notification.background.ts
+++ b/apps/browser/src/autofill/background/abstractions/notification.background.ts
@@ -101,6 +101,10 @@ type NotificationBackgroundExtensionMessageHandlers = {
   bgChangedPassword: ({ message, sender }: BackgroundOnMessageHandlerParams) => Promise<void>;
   bgRemoveTabFromNotificationQueue: ({ sender }: BackgroundSenderParam) => void;
   bgSaveCipher: ({ message, sender }: BackgroundOnMessageHandlerParams) => void;
+  bgOpenViewVaultItemPopout: ({
+    message,
+    sender,
+  }: BackgroundOnMessageHandlerParams) => Promise<void>;
   bgOpenVault: ({ message, sender }: BackgroundOnMessageHandlerParams) => Promise<void>;
   bgNeverSave: ({ sender }: BackgroundSenderParam) => Promise<void>;
   bgUnlockPopoutOpened: ({ message, sender }: BackgroundOnMessageHandlerParams) => Promise<void>;
diff --git a/apps/browser/src/autofill/background/notification.background.spec.ts b/apps/browser/src/autofill/background/notification.background.spec.ts
index ffc416ab62a1..bb993fcf94b0 100644
--- a/apps/browser/src/autofill/background/notification.background.spec.ts
+++ b/apps/browser/src/autofill/background/notification.background.spec.ts
@@ -823,6 +823,7 @@ describe("NotificationBackground", () => {
           notificationBackground["notificationQueue"] = [queueMessage];
           const cipherView = mock<CipherView>({
             id: "testId",
+            name: "testItemName",
             login: { username: "testUser" },
           });
           getDecryptedCipherByIdSpy.mockResolvedValueOnce(cipherView);
@@ -844,8 +845,9 @@ describe("NotificationBackground", () => {
             sender.tab,
             "saveCipherAttemptCompleted",
             {
-              username: cipherView.login.username,
+              itemName: "testItemName",
               cipherId: cipherView.id,
+              task: undefined,
             },
           );
         });
@@ -899,7 +901,7 @@ describe("NotificationBackground", () => {
           const cipherView = mock<CipherView>({
             id: mockCipherId,
             organizationId: mockOrgId,
-            login: { username: "testUser" },
+            name: "Test Item",
           });
           getDecryptedCipherByIdSpy.mockResolvedValueOnce(cipherView);
 
@@ -921,11 +923,11 @@ describe("NotificationBackground", () => {
             "saveCipherAttemptCompleted",
             {
               cipherId: "testId",
+              itemName: "Test Item",
               task: {
                 orgName: "Org Name, LLC",
                 remainingTasksCount: 1,
               },
-              username: "testUser",
             },
           );
         });
@@ -1074,6 +1076,7 @@ describe("NotificationBackground", () => {
           notificationBackground["notificationQueue"] = [queueMessage];
           const cipherView = mock<CipherView>({
             id: "testId",
+            name: "testName",
             login: { username: "test", password: "password" },
           });
           folderExistsSpy.mockResolvedValueOnce(false);
@@ -1097,8 +1100,8 @@ describe("NotificationBackground", () => {
             sender.tab,
             "saveCipherAttemptCompleted",
             {
-              username: cipherView.login.username,
               cipherId: cipherView.id,
+              itemName: cipherView.name,
             },
           );
           expect(tabSendMessageSpy).toHaveBeenCalledWith(sender.tab, { command: "addedCipher" });
diff --git a/apps/browser/src/autofill/background/notification.background.ts b/apps/browser/src/autofill/background/notification.background.ts
index 6589252d94bd..54b9f54c5814 100644
--- a/apps/browser/src/autofill/background/notification.background.ts
+++ b/apps/browser/src/autofill/background/notification.background.ts
@@ -41,7 +41,10 @@ import { SecurityTask } from "@bitwarden/common/vault/tasks/models/security-task
 
 import { openUnlockPopout } from "../../auth/popup/utils/auth-popout-window";
 import { BrowserApi } from "../../platform/browser/browser-api";
-import { openAddEditVaultItemPopout } from "../../vault/popup/utils/vault-popout-window";
+import {
+  openAddEditVaultItemPopout,
+  openViewVaultItemPopout,
+} from "../../vault/popup/utils/vault-popout-window";
 import {
   OrganizationCategory,
   OrganizationCategories,
@@ -67,6 +70,7 @@ import { OverlayBackgroundExtensionMessage } from "./abstractions/overlay.backgr
 export default class NotificationBackground {
   private openUnlockPopout = openUnlockPopout;
   private openAddEditVaultItemPopout = openAddEditVaultItemPopout;
+  private openViewVaultItemPopout = openViewVaultItemPopout;
   private notificationQueue: NotificationQueueMessageItem[] = [];
   private allowedRetryCommands: Set<ExtensionCommandType> = new Set([
     ExtensionCommand.AutofillLogin,
@@ -91,6 +95,7 @@ export default class NotificationBackground {
     bgGetOrgData: () => this.getOrgData(),
     bgNeverSave: ({ sender }) => this.saveNever(sender.tab),
     bgOpenVault: ({ message, sender }) => this.openVault(message, sender.tab),
+    bgOpenViewVaultItemPopout: ({ message, sender }) => this.viewItem(message, sender.tab),
     bgRemoveTabFromNotificationQueue: ({ sender }) =>
       this.removeTabFromNotificationQueue(sender.tab),
     bgReopenUnlockPopout: ({ sender }) => this.openUnlockPopout(sender.tab),
@@ -638,8 +643,8 @@ export default class NotificationBackground {
       try {
         await this.cipherService.createWithServer(cipher);
         await BrowserApi.tabSendMessageData(tab, "saveCipherAttemptCompleted", {
-          username: queueMessage?.username && String(queueMessage.username),
-          cipherId: cipher?.id && String(cipher.id),
+          itemName: newCipher?.name && String(newCipher?.name),
+          cipherId: cipher?.id && String(cipher?.id),
         });
         await BrowserApi.tabSendMessage(tab, { command: "addedCipher" });
       } catch (error) {
@@ -701,7 +706,7 @@ export default class NotificationBackground {
       await this.cipherService.updateWithServer(cipher);
 
       await BrowserApi.tabSendMessageData(tab, "saveCipherAttemptCompleted", {
-        username: cipherView?.login?.username && String(cipherView.login.username),
+        itemName: cipherView?.name && String(cipherView?.name),
         cipherId: cipherView?.id && String(cipherView.id),
         task: taskData,
       });
@@ -754,6 +759,21 @@ export default class NotificationBackground {
     await this.openAddEditVaultItemPopout(senderTab, { cipherId: message.cipherId });
   }
 
+  private async viewItem(
+    message: NotificationBackgroundExtensionMessage,
+    senderTab: chrome.tabs.Tab,
+  ) {
+    await Promise.all([
+      this.openViewVaultItemPopout(senderTab, {
+        cipherId: message.cipherId,
+        action: null,
+      }),
+      BrowserApi.tabSendMessageData(senderTab, "closeNotificationBar", {
+        fadeOutNotification: !!message.fadeOutNotification,
+      }),
+    ]);
+  }
+
   private async folderExists(folderId: string, userId: UserId) {
     if (Utils.isNullOrWhitespace(folderId) || folderId === "null") {
       return false;
diff --git a/apps/browser/src/autofill/content/components/notification/confirmation/body.ts b/apps/browser/src/autofill/content/components/notification/confirmation/body.ts
index d2ac7f362775..0508991c5da0 100644
--- a/apps/browser/src/autofill/content/components/notification/confirmation/body.ts
+++ b/apps/browser/src/autofill/content/components/notification/confirmation/body.ts
@@ -16,16 +16,18 @@ const { css } = createEmotion({
 
 export type NotificationConfirmationBodyProps = {
   buttonText: string;
+  itemName: string;
   confirmationMessage: string;
   error?: string;
   messageDetails?: string;
   tasksAreComplete?: boolean;
   theme: Theme;
-  handleOpenVault: (e: Event) => void;
+  handleOpenVault: () => void;
 };
 
 export function NotificationConfirmationBody({
   buttonText,
+  itemName,
   confirmationMessage,
   error,
   messageDetails,
@@ -43,6 +45,7 @@ export function NotificationConfirmationBody({
       ${showConfirmationMessage
         ? NotificationConfirmationMessage({
             buttonText,
+            itemName,
             message: confirmationMessage,
             messageDetails,
             theme,
diff --git a/apps/browser/src/autofill/content/components/notification/confirmation/container.ts b/apps/browser/src/autofill/content/components/notification/confirmation/container.ts
index a071338af9af..5cc977cf4cbd 100644
--- a/apps/browser/src/autofill/content/components/notification/confirmation/container.ts
+++ b/apps/browser/src/autofill/content/components/notification/confirmation/container.ts
@@ -20,14 +20,14 @@ import { NotificationConfirmationFooter } from "./footer";
 
 export type NotificationConfirmationContainerProps = NotificationBarIframeInitData & {
   handleCloseNotification: (e: Event) => void;
-  handleOpenVault: (e: Event) => void;
+  handleOpenVault: () => void;
   handleOpenTasks: (e: Event) => void;
 } & {
   error?: string;
   i18n: { [key: string]: string };
+  itemName: string;
   task?: NotificationTaskInfo;
   type: NotificationType;
-  username: string;
 };
 
 export function NotificationConfirmationContainer({
@@ -36,13 +36,13 @@ export function NotificationConfirmationContainer({
   handleOpenVault,
   handleOpenTasks,
   i18n,
+  itemName,
   task,
   theme = ThemeTypes.Light,
   type,
-  username,
 }: NotificationConfirmationContainerProps) {
   const headerMessage = getHeaderMessage(i18n, type, error);
-  const confirmationMessage = getConfirmationMessage(i18n, username, type, error);
+  const confirmationMessage = getConfirmationMessage(i18n, itemName, type, error);
   const buttonText = error ? i18n.newItem : i18n.view;
 
   let messageDetails: string | undefined;
@@ -71,6 +71,7 @@ export function NotificationConfirmationContainer({
       })}
       ${NotificationConfirmationBody({
         buttonText,
+        itemName,
         confirmationMessage,
         tasksAreComplete,
         messageDetails,
@@ -106,19 +107,17 @@ const notificationContainerStyles = (theme: Theme) => css`
 
 function getConfirmationMessage(
   i18n: { [key: string]: string },
-  username: string,
+  itemName: string,
   type?: NotificationType,
   error?: string,
 ) {
-  const loginSaveSuccessDetails = chrome.i18n.getMessage("loginSaveSuccessDetails", [username]);
-  const loginUpdatedSuccessDetails = chrome.i18n.getMessage("loginUpdatedSuccessDetails", [
-    username,
-  ]);
+  const loginSaveConfirmation = chrome.i18n.getMessage("loginSaveConfirmation", [itemName]);
+  const loginUpdatedConfirmation = chrome.i18n.getMessage("loginUpdatedConfirmation", [itemName]);
 
   if (error) {
     return i18n.saveFailureDetails;
   }
-  return type === "add" ? loginSaveSuccessDetails : loginUpdatedSuccessDetails;
+  return type === "add" ? loginSaveConfirmation : loginUpdatedConfirmation;
 }
 
 function getHeaderMessage(
diff --git a/apps/browser/src/autofill/content/components/notification/confirmation/message.ts b/apps/browser/src/autofill/content/components/notification/confirmation/message.ts
index c018371caffc..3707e628370e 100644
--- a/apps/browser/src/autofill/content/components/notification/confirmation/message.ts
+++ b/apps/browser/src/autofill/content/components/notification/confirmation/message.ts
@@ -7,19 +7,23 @@ import { themes, typography } from "../../constants/styles";
 
 export type NotificationConfirmationMessageProps = {
   buttonText?: string;
+  itemName: string;
   message?: string;
   messageDetails?: string;
-  handleClick: (e: Event) => void;
+  handleClick: () => void;
   theme: Theme;
 };
 
 export function NotificationConfirmationMessage({
   buttonText,
+  itemName,
   message,
   messageDetails,
   handleClick,
   theme,
 }: NotificationConfirmationMessageProps) {
+  const buttonAria = chrome.i18n.getMessage("notificationViewAria", [itemName]);
+
   return html`
     <div>
       ${message || buttonText
@@ -35,6 +39,10 @@ export function NotificationConfirmationMessage({
                       title=${buttonText}
                       class=${notificationConfirmationButtonTextStyles(theme)}
                       @click=${handleClick}
+                      @keydown=${(e: KeyboardEvent) => handleButtonKeyDown(e, handleClick)}
+                      aria-label=${buttonAria}
+                      tabindex="0"
+                      role="button"
                     >
                       ${buttonText}
                     </a>
@@ -81,3 +89,10 @@ const AdditionalMessageStyles = ({ theme }: { theme: Theme }) => css`
   font-size: 14px;
   color: ${themes[theme].text.muted};
 `;
+
+function handleButtonKeyDown(event: KeyboardEvent, handleClick: () => void) {
+  if (event.key === "Enter" || event.key === " ") {
+    event.preventDefault();
+    handleClick();
+  }
+}
diff --git a/apps/browser/src/autofill/notification/abstractions/notification-bar.ts b/apps/browser/src/autofill/notification/abstractions/notification-bar.ts
index cbfeffcf2f4a..9dd02b641545 100644
--- a/apps/browser/src/autofill/notification/abstractions/notification-bar.ts
+++ b/apps/browser/src/autofill/notification/abstractions/notification-bar.ts
@@ -33,7 +33,7 @@ type NotificationBarWindowMessage = {
   data?: {
     cipherId?: string;
     task?: NotificationTaskInfo;
-    username?: string;
+    itemName?: string;
   };
   error?: string;
   initData?: NotificationBarIframeInitData;
diff --git a/apps/browser/src/autofill/notification/bar.ts b/apps/browser/src/autofill/notification/bar.ts
index d660790ee63e..aa0a61530be2 100644
--- a/apps/browser/src/autofill/notification/bar.ts
+++ b/apps/browser/src/autofill/notification/bar.ts
@@ -55,9 +55,9 @@ function getI18n() {
     close: chrome.i18n.getMessage("close"),
     folder: chrome.i18n.getMessage("folder"),
     loginSaveSuccess: chrome.i18n.getMessage("loginSaveSuccess"),
-    loginSaveSuccessDetails: chrome.i18n.getMessage("loginSaveSuccessDetails"),
+    loginSaveConfirmation: chrome.i18n.getMessage("loginSaveConfirmation"),
     loginUpdateSuccess: chrome.i18n.getMessage("loginUpdateSuccess"),
-    loginUpdateSuccessDetails: chrome.i18n.getMessage("loginUpdatedSuccessDetails"),
+    loginUpdateConfirmation: chrome.i18n.getMessage("loginUpdatedConfirmation"),
     loginUpdateTaskSuccess: chrome.i18n.getMessage("loginUpdateTaskSuccess"),
     loginUpdateTaskSuccessAdditional: chrome.i18n.getMessage("loginUpdateTaskSuccessAdditional"),
     nextSecurityTaskAction: chrome.i18n.getMessage("nextSecurityTaskAction"),
@@ -70,6 +70,7 @@ function getI18n() {
     notificationEdit: chrome.i18n.getMessage("edit"),
     notificationUnlock: chrome.i18n.getMessage("notificationUnlock"),
     notificationUnlockDesc: chrome.i18n.getMessage("notificationUnlockDesc"),
+    notificationViewAria: chrome.i18n.getMessage("notificationViewAria"),
     saveAction: chrome.i18n.getMessage("notificationAddSave"),
     saveAsNewLoginAction: chrome.i18n.getMessage("saveAsNewLoginAction"),
     saveFailure: chrome.i18n.getMessage("saveFailure"),
@@ -346,10 +347,9 @@ function handleSaveCipherAttemptCompletedMessage(message: NotificationBarWindowM
   );
 }
 
-function openViewVaultItemPopout(e: Event, cipherId: string) {
-  e.preventDefault();
+function openViewVaultItemPopout(cipherId: string) {
   sendPlatformMessage({
-    command: "bgOpenVault",
+    command: "bgOpenViewVaultItemPopout",
     cipherId,
   });
 }
@@ -357,7 +357,7 @@ function openViewVaultItemPopout(e: Event, cipherId: string) {
 function handleSaveCipherConfirmation(message: NotificationBarWindowMessage) {
   const { theme, type } = notificationBarIframeInitData;
   const { error, data } = message;
-  const { username, cipherId, task } = data || {};
+  const { cipherId, task, itemName } = data || {};
   const i18n = getI18n();
   const resolvedTheme = getResolvedTheme(theme ?? ThemeTypes.Light);
 
@@ -371,9 +371,9 @@ function handleSaveCipherConfirmation(message: NotificationBarWindowMessage) {
       handleCloseNotification,
       i18n,
       error,
-      username: username ?? i18n.typeLogin,
+      itemName: itemName ?? i18n.typeLogin,
       task,
-      handleOpenVault: (e) => cipherId && openViewVaultItemPopout(e, cipherId),
+      handleOpenVault: () => cipherId && openViewVaultItemPopout(cipherId),
       handleOpenTasks: () => sendPlatformMessage({ command: "bgOpenAtRisksPasswords" }),
     }),
     document.body,


Reviews:
user: login: jprusik
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-22T20:35:22Z
user: login: dan-livefront
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-22T20:37:16Z
user: login: jprusik
state: DISMISSED
author_association: CONTRIBUTOR
submitted_at: 2025-04-22T21:14:49Z
user: login: danielleflinn
body: Screenshots look as expected
state: DISMISSED
author_association: CONTRIBUTOR
submitted_at: 2025-04-22T21:23:19Z
user: login: jprusik
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-04-23T18:46:36Z

Review comments:
diff_hunk: @@ -638,8 +643,8 @@ export default class NotificationBackground {
       try {
         await this.cipherService.createWithServer(cipher);
         await BrowserApi.tabSendMessageData(tab, "saveCipherAttemptCompleted", {
-          username: queueMessage?.username && String(queueMessage.username),
-          cipherId: cipher?.id && String(cipher.id),
+          itemName: newCipher?.name && String(newCipher?.name),
path: apps/browser/src/autofill/background/notification.background.ts
user: login: jprusik
body: `newCipher` here because it's [encrypted in `cipher` at this point](https://github.com/bitwarden/clients/pull/14342/files#diff-762d14964781929dd1d18d5717f61486d98bedda66d42e4f9f8159accef3befcL637), yeah?
created_at: 2025-04-22T20:35:22Z
author_association: CONTRIBUTOR
diff_hunk: @@ -638,8 +643,8 @@ export default class NotificationBackground {
       try {
         await this.cipherService.createWithServer(cipher);
         await BrowserApi.tabSendMessageData(tab, "saveCipherAttemptCompleted", {
-          username: queueMessage?.username && String(queueMessage.username),
-          cipherId: cipher?.id && String(cipher.id),
+          itemName: newCipher?.name && String(newCipher?.name),
path: apps/browser/src/autofill/background/notification.background.ts
user: login: dan-livefront
body: Yep!
created_at: 2025-04-22T20:37:16Z
author_association: COLLABORATOR

PR comments:
user: login: codecov[bot]
created_at: 2025-04-18T18:11:24Z
body: ## [Codecov](https://app.codecov.io/gh/bitwarden/clients/pull/14342?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=bitwarden) Report
Attention: Patch coverage is `10.52632%` with `17 lines` in your changes missing coverage. Please review.
> Project coverage is 36.18%. Comparing base [(`c08888b`)](https://app.codecov.io/gh/bitwarden/clients/commit/c08888bbd963e70784d58053e22da6f9b09a1442?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=bitwarden) to head [(`e652d32`)](https://app.codecov.io/gh/bitwarden/clients/commit/e652d32f3988f490c200c20f2126917b2ac56f26?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=bitwarden).
> Report is 8 commits behind head on main.

:white_check_mark: All tests successful. No failed tests found.

| [Files with missing lines](https://app.codecov.io/gh/bitwarden/clients/pull/14342?dropdown=coverage&src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=bitwarden) | Patch % | Lines |
|---|---|---|
| [...src/autofill/background/notification.background.ts](https://app.codecov.io/gh/bitwarden/clients/pull/14342?src=pr&el=tree&filepath=apps%2Fbrowser%2Fsrc%2Fautofill%2Fbackground%2Fnotification.background.ts&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=bitwarden#diff-YXBwcy9icm93c2VyL3NyYy9hdXRvZmlsbC9iYWNrZ3JvdW5kL25vdGlmaWNhdGlvbi5iYWNrZ3JvdW5kLnRz) | 28.57% | [2 Missing and 3 partials :warning: ](https://app.codecov.io/gh/bitwarden/clients/pull/14342?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=bitwarden) |
| [...nt/components/notification/confirmation/message.ts](https://app.codecov.io/gh/bitwarden/clients/pull/14342?src=pr&el=tree&filepath=apps%2Fbrowser%2Fsrc%2Fautofill%2Fcontent%2Fcomponents%2Fnotification%2Fconfirmation%2Fmessage.ts&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=bitwarden#diff-YXBwcy9icm93c2VyL3NyYy9hdXRvZmlsbC9jb250ZW50L2NvbXBvbmVudHMvbm90aWZpY2F0aW9uL2NvbmZpcm1hdGlvbi9tZXNzYWdlLnRz) | 0.00% | [5 Missing :warning: ](https://app.codecov.io/gh/bitwarden/clients/pull/14342?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=bitwarden) |
| [.../components/notification/confirmation/container.ts](https://app.codecov.io/gh/bitwarden/clients/pull/14342?src=pr&el=tree&filepath=apps%2Fbrowser%2Fsrc%2Fautofill%2Fcontent%2Fcomponents%2Fnotification%2Fconfirmation%2Fcontainer.ts&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=bitwarden#diff-YXBwcy9icm93c2VyL3NyYy9hdXRvZmlsbC9jb250ZW50L2NvbXBvbmVudHMvbm90aWZpY2F0aW9uL2NvbmZpcm1hdGlvbi9jb250YWluZXIudHM=) | 0.00% | [4 Missing :warning: ](https://app.codecov.io/gh/bitwarden/clients/pull/14342?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=bitwarden) |
| [apps/browser/src/autofill/notification/bar.ts](https://app.codecov.io/gh/bitwarden/clients/pull/14342?src=pr&el=tree&filepath=apps%2Fbrowser%2Fsrc%2Fautofill%2Fnotification%2Fbar.ts&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=bitwarden#diff-YXBwcy9icm93c2VyL3NyYy9hdXRvZmlsbC9ub3RpZmljYXRpb24vYmFyLnRz) | 0.00% | [3 Missing :warning: ](https://app.codecov.io/gh/bitwarden/clients/pull/14342?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=bitwarden) |

<details><summary>Additional details and impacted files</summary>


```diff
@@            Coverage Diff             @@
##             main   #14342      +/-   ##
==========================================
- Coverage   36.18%   36.18%   -0.01%     
==========================================
  Files        3205     3205              
  Lines       91693    91700       +7     
  Branches    16408    16409       +1     
==========================================
+ Hits        33179    33180       +1     
- Misses      56141    56147       +6     
  Partials     2373     2373              
```

</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/bitwarden/clients/pull/14342?dropdown=coverage&src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=bitwarden).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=bitwarden).

<details><summary> :rocket: New features to boost your workflow: </summary>

- :package: [JS Bundle Analysis](https://docs.codecov.com/docs/javascript-bundle-analysis): Save yourself from yourself by tracking and limiting bundle sizes in JS merges.
</details>
author_association: NONE
user: login: github-actions[bot]
created_at: 2025-04-18T18:18:11Z
body: ![Logo](https://cdn.ast.checkmarx.net/integrations/logo/Checkmarx.png)
**Checkmarx One – Scan Summary & Details** – [4aec44c5\-7478\-46d9\-8f7d\-c5cb18f5c175](https://ast.checkmarx.net/projects/41bd746e-d417-4eda-afeb-01a59d162661/scans?id=4aec44c5-7478-46d9-8f7d-c5cb18f5c175&branch=PM-20396-ux-confirmation-ui)
### Great job, no security vulnerabilities found in this Pull Request

author_association: CONTRIBUTOR
user: login: sonarqubecloud[bot]
created_at: 2025-04-22T21:52:32Z
body: ## [![Quality Gate Passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/qg-passed-20px.png 'Quality Gate Passed')](https://sonarcloud.io/dashboard?id=bitwarden_clients&pullRequest=14342) **Quality Gate passed**  
Issues  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 New issues](https://sonarcloud.io/project/issues?id=bitwarden_clients&pullRequest=14342&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/accepted-16px.png '') [0 Accepted issues](https://sonarcloud.io/project/issues?id=bitwarden_clients&pullRequest=14342&issueStatuses=ACCEPTED)

Measures  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=bitwarden_clients&pullRequest=14342&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Coverage on New Code](https://sonarcloud.io/component_measures?id=bitwarden_clients&pullRequest=14342&metric=new_coverage&view=list)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Duplication on New Code](https://sonarcloud.io/component_measures?id=bitwarden_clients&pullRequest=14342&metric=new_duplicated_lines_density&view=list)  
  
[See analysis details on SonarQube Cloud](https://sonarcloud.io/dashboard?id=bitwarden_clients&pullRequest=14342)


author_association: NONE