diff --git a/apps/browser/src/_locales/en/messages.json b/apps/browser/src/_locales/en/messages.json index f3b85496b75..87b94650b51 100644 --- a/apps/browser/src/_locales/en/messages.json +++ b/apps/browser/src/_locales/en/messages.json @@ -4928,8 +4928,8 @@ "message": "Password regenerated", "description": "Notification message for when a password has been regenerated" }, - "saveLoginToBitwarden": { - "message": "Save login to Bitwarden?", + "saveToBitwarden": { + "message": "Save to Bitwarden", "description": "Confirmation message for saving a login to Bitwarden" }, "spaceCharacterDescriptor": { diff --git a/apps/browser/src/autofill/background/overlay.background.ts b/apps/browser/src/autofill/background/overlay.background.ts index a2088f50a11..4e2e773a0c7 100644 --- a/apps/browser/src/autofill/background/overlay.background.ts +++ b/apps/browser/src/autofill/background/overlay.background.ts @@ -1852,7 +1852,7 @@ export class OverlayBackground implements OverlayBackgroundInterface { /** * Verifies whether the save login inline menu view should be shown. This requires that - * the login data on the page contains a username and either a current or new password. + * the login data on the page contains either a current or new password. * * @param tab - The tab to check for login data */ @@ -1869,7 +1869,7 @@ export class OverlayBackground implements OverlayBackgroundInterface { return ( (this.shouldShowInlineMenuAccountCreation() || this.focusedFieldMatchesFillType(InlineMenuFillType.PasswordGeneration)) && - !!(loginData.username && (loginData.password || loginData.newPassword)) + !!(loginData.password || loginData.newPassword) ); } @@ -2157,7 +2157,7 @@ export class OverlayBackground implements OverlayBackgroundInterface { "passwordRegenerated", "passwords", "regeneratePassword", - "saveLoginToBitwarden", + "saveToBitwarden", "toggleBitwardenVaultOverlay", "totpCodeAria", "totpSecondsSpanAria", diff --git a/apps/browser/src/autofill/overlay/inline-menu/pages/list/__snapshots__/autofill-inline-menu-list.spec.ts.snap b/apps/browser/src/autofill/overlay/inline-menu/pages/list/__snapshots__/autofill-inline-menu-list.spec.ts.snap index acd06fb8c65..b6e41c448d6 100644 --- a/apps/browser/src/autofill/overlay/inline-menu/pages/list/__snapshots__/autofill-inline-menu-list.spec.ts.snap +++ b/apps/browser/src/autofill/overlay/inline-menu/pages/list/__snapshots__/autofill-inline-menu-list.spec.ts.snap @@ -4,47 +4,14 @@ exports[`AutofillInlineMenuList initAutofillInlineMenuList creates the build sav
`; diff --git a/apps/browser/src/autofill/overlay/inline-menu/pages/list/autofill-inline-menu-list.spec.ts b/apps/browser/src/autofill/overlay/inline-menu/pages/list/autofill-inline-menu-list.spec.ts index b1eebd2bc39..ed28375e4fe 100644 --- a/apps/browser/src/autofill/overlay/inline-menu/pages/list/autofill-inline-menu-list.spec.ts +++ b/apps/browser/src/autofill/overlay/inline-menu/pages/list/autofill-inline-menu-list.spec.ts @@ -1089,12 +1089,12 @@ describe("AutofillInlineMenuList", () => { }); describe("displaying the save login view", () => { - let buildSaveLoginInlineMenuListSpy: jest.SpyInstance; + let buildSaveLoginInlineMenuSpy: jest.SpyInstance; beforeEach(() => { - buildSaveLoginInlineMenuListSpy = jest.spyOn( + buildSaveLoginInlineMenuSpy = jest.spyOn( autofillInlineMenuList as any, - "buildSaveLoginInlineMenuList", + "buildSaveLoginInlineMenu", ); }); @@ -1108,7 +1108,7 @@ describe("AutofillInlineMenuList", () => { postWindowMessage({ command: "showSaveLoginInlineMenuList" }); - expect(buildSaveLoginInlineMenuListSpy).not.toHaveBeenCalled(); + expect(buildSaveLoginInlineMenuSpy).not.toHaveBeenCalled(); }); it("builds the save login item view", async () => { @@ -1117,7 +1117,7 @@ describe("AutofillInlineMenuList", () => { postWindowMessage({ command: "showSaveLoginInlineMenuList" }); - expect(buildSaveLoginInlineMenuListSpy).toHaveBeenCalled(); + expect(buildSaveLoginInlineMenuSpy).toHaveBeenCalled(); }); }); diff --git a/apps/browser/src/autofill/overlay/inline-menu/pages/list/autofill-inline-menu-list.ts b/apps/browser/src/autofill/overlay/inline-menu/pages/list/autofill-inline-menu-list.ts index acb01594cc6..e0db93b6b4a 100644 --- a/apps/browser/src/autofill/overlay/inline-menu/pages/list/autofill-inline-menu-list.ts +++ b/apps/browser/src/autofill/overlay/inline-menu/pages/list/autofill-inline-menu-list.ts @@ -3,6 +3,8 @@ import "@webcomponents/custom-elements"; import "lit/polyfill-support.js"; +import { FocusableElement } from "tabbable"; + import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status"; import { EVENTS, UPDATE_PASSKEYS_HEADINGS_ON_SCROLL } from "@bitwarden/common/autofill/constants"; import { CipherRepromptType, CipherType } from "@bitwarden/common/vault/enums"; @@ -117,7 +119,7 @@ export class AutofillInlineMenuList extends AutofillInlineMenuPageElement { } if (showSaveLoginMenu) { - this.buildSaveLoginInlineMenuList(); + this.buildSaveLoginInlineMenu(); return; } @@ -165,24 +167,52 @@ export class AutofillInlineMenuList extends AutofillInlineMenuPageElement { /** * Builds the inline menu list as a prompt that asks the user if they'd like to save the login data. */ - private buildSaveLoginInlineMenuList() { - const saveLoginMessage = globalThis.document.createElement("div"); - saveLoginMessage.classList.add("save-login", "inline-menu-list-message"); - saveLoginMessage.textContent = this.getTranslation("saveLoginToBitwarden"); + private buildSaveLoginInlineMenu() { + const saveLoginButton = globalThis.document.createElement("button"); + saveLoginButton.classList.add( + "save-login", + "inline-menu-list-button", + "inline-menu-list-action", + ); + + saveLoginButton.tabIndex = -1; + saveLoginButton.setAttribute( + "aria-label", + `${this.getTranslation("saveToBitwarden")}, ${this.getTranslation("opensInANewWindow")}`, + ); + saveLoginButton.textContent = this.getTranslation("saveToBitwarden"); + + saveLoginButton.addEventListener(EVENTS.CLICK, this.handleNewLoginVaultItemAction); + saveLoginButton.addEventListener(EVENTS.KEYUP, this.handleSaveLoginInlineMenuKeyUp); + + const inlineMenuListButtonContainer = this.buildButtonContainer(saveLoginButton); - const newItemButton = this.buildNewItemButton(true); this.showInlineMenuAccountCreation = true; - this.inlineMenuListContainer.append(saveLoginMessage, newItemButton); + this.inlineMenuListContainer.append(inlineMenuListButtonContainer); } + private handleSaveLoginInlineMenuKeyUp = (event: KeyboardEvent) => { + const listenedForKeys = new Set(["ArrowDown"]); + if (!listenedForKeys.has(event.code) || !(event.target instanceof Element)) { + return; + } + + event.preventDefault(); + + if (event.code === "ArrowDown") { + (event.target as FocusableElement).focus(); + return; + } + }; + /** * Handles the show save login inline menu list message that is triggered from the background script. */ private handleShowSaveLoginInlineMenuList() { if (this.authStatus === AuthenticationStatus.Unlocked) { this.resetInlineMenuContainer(); - this.buildSaveLoginInlineMenuList(); + this.buildSaveLoginInlineMenu(); } } @@ -521,7 +551,7 @@ export class AutofillInlineMenuList extends AutofillInlineMenuPageElement { this.newItemButtonElement.textContent = this.getNewItemButtonText(showLogin); this.newItemButtonElement.setAttribute("aria-label", this.getNewItemAriaLabel(showLogin)); this.newItemButtonElement.prepend(buildSvgDomElement(plusIcon)); - this.newItemButtonElement.addEventListener(EVENTS.CLICK, this.handeNewItemButtonClick); + this.newItemButtonElement.addEventListener(EVENTS.CLICK, this.handleNewLoginVaultItemAction); return this.buildButtonContainer(this.newItemButtonElement); } @@ -581,7 +611,7 @@ export class AutofillInlineMenuList extends AutofillInlineMenuPageElement { * Handles the click event for the new item button. * Sends a message to the parent window to add a new vault item. */ - private handeNewItemButtonClick = () => { + private handleNewLoginVaultItemAction = () => { let addNewCipherType = this.inlineMenuFillType; if (this.showInlineMenuAccountCreation) { diff --git a/apps/browser/src/autofill/overlay/inline-menu/pages/list/list.scss b/apps/browser/src/autofill/overlay/inline-menu/pages/list/list.scss index d0875cfe427..93f5f647ffe 100644 --- a/apps/browser/src/autofill/overlay/inline-menu/pages/list/list.scss +++ b/apps/browser/src/autofill/overlay/inline-menu/pages/list/list.scss @@ -45,6 +45,14 @@ body * { &.no-items, &.save-login { font-size: 1.6rem; + &:has(:focus-visible) { + outline-width: 0.2rem; + outline-style: solid; + + @include themify($themes) { + outline-color: themed("focusOutlineColor"); + } + } } }