mirror of
https://github.com/bitwarden/browser
synced 2025-12-12 06:13:38 +00:00
[PM-15436] Standalone password entry should trigger save to bitwarden prompt. (#14110)
* Modify behavior so standalone password entry (with or without generator) should trigger save to bitwarden prompt. * Rename intent to action, extend button/action styles. * Ensure font weight is returned to normal. * Make save login message a button to handle accessibility, adds helper function. * Fix failing snapshot by reintigrating erroneously removed line. * Update snapshot to match new saveLoginButton. * Add add'l open in new window message to aria label. * Update snapshot with open in new window message.
This commit is contained in:
@@ -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": {
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -4,47 +4,14 @@ exports[`AutofillInlineMenuList initAutofillInlineMenuList creates the build sav
|
||||
<div
|
||||
class="inline-menu-list-container theme_light"
|
||||
>
|
||||
<div
|
||||
class="save-login inline-menu-list-message"
|
||||
/>
|
||||
<div
|
||||
class="inline-menu-list-button-container"
|
||||
>
|
||||
<button
|
||||
aria-label=""
|
||||
class="add-new-item-button inline-menu-list-button inline-menu-list-action"
|
||||
id="new-item-button"
|
||||
aria-label=", opensInANewWindow"
|
||||
class="save-login inline-menu-list-button inline-menu-list-action"
|
||||
tabindex="-1"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
fill="none"
|
||||
height="17"
|
||||
width="17"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<g
|
||||
clip-path="url(#a)"
|
||||
>
|
||||
<path
|
||||
clip-rule="evenodd"
|
||||
d="M9.607 7.15h5.35c.322 0 .627.133.847.362a1.213 1.213 0 0 1 .002 1.68c-.221.23-.527.363-.85.363H9.607v5.652c0 .312-.12.613-.336.839a1.176 1.176 0 0 1-1.696.003 1.21 1.21 0 0 1-.34-.842V9.555H1.888a1.173 1.173 0 0 1-.847-.361A1.193 1.193 0 0 1 .7 8.352a1.219 1.219 0 0 1 .336-.838 1.175 1.175 0 0 1 .85-.364h5.349V1.635c0-.31.118-.611.336-.84A1.176 1.176 0 0 1 9.268.795c.222.228.34.533.34.841V7.15Z"
|
||||
fill="#175DDC"
|
||||
fill-rule="evenodd"
|
||||
/>
|
||||
</g>
|
||||
<defs>
|
||||
<clippath
|
||||
id="a"
|
||||
>
|
||||
<path
|
||||
d="M.421.421h16v16h-16z"
|
||||
fill="#fff"
|
||||
/>
|
||||
</clippath>
|
||||
</defs>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
@@ -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();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user