mirror of
https://github.com/bitwarden/browser
synced 2025-12-12 14:23:32 +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",
|
"message": "Password regenerated",
|
||||||
"description": "Notification message for when a password has been regenerated"
|
"description": "Notification message for when a password has been regenerated"
|
||||||
},
|
},
|
||||||
"saveLoginToBitwarden": {
|
"saveToBitwarden": {
|
||||||
"message": "Save login to Bitwarden?",
|
"message": "Save to Bitwarden",
|
||||||
"description": "Confirmation message for saving a login to Bitwarden"
|
"description": "Confirmation message for saving a login to Bitwarden"
|
||||||
},
|
},
|
||||||
"spaceCharacterDescriptor": {
|
"spaceCharacterDescriptor": {
|
||||||
|
|||||||
@@ -1852,7 +1852,7 @@ export class OverlayBackground implements OverlayBackgroundInterface {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Verifies whether the save login inline menu view should be shown. This requires that
|
* 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
|
* @param tab - The tab to check for login data
|
||||||
*/
|
*/
|
||||||
@@ -1869,7 +1869,7 @@ export class OverlayBackground implements OverlayBackgroundInterface {
|
|||||||
return (
|
return (
|
||||||
(this.shouldShowInlineMenuAccountCreation() ||
|
(this.shouldShowInlineMenuAccountCreation() ||
|
||||||
this.focusedFieldMatchesFillType(InlineMenuFillType.PasswordGeneration)) &&
|
this.focusedFieldMatchesFillType(InlineMenuFillType.PasswordGeneration)) &&
|
||||||
!!(loginData.username && (loginData.password || loginData.newPassword))
|
!!(loginData.password || loginData.newPassword)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2157,7 +2157,7 @@ export class OverlayBackground implements OverlayBackgroundInterface {
|
|||||||
"passwordRegenerated",
|
"passwordRegenerated",
|
||||||
"passwords",
|
"passwords",
|
||||||
"regeneratePassword",
|
"regeneratePassword",
|
||||||
"saveLoginToBitwarden",
|
"saveToBitwarden",
|
||||||
"toggleBitwardenVaultOverlay",
|
"toggleBitwardenVaultOverlay",
|
||||||
"totpCodeAria",
|
"totpCodeAria",
|
||||||
"totpSecondsSpanAria",
|
"totpSecondsSpanAria",
|
||||||
|
|||||||
@@ -4,47 +4,14 @@ exports[`AutofillInlineMenuList initAutofillInlineMenuList creates the build sav
|
|||||||
<div
|
<div
|
||||||
class="inline-menu-list-container theme_light"
|
class="inline-menu-list-container theme_light"
|
||||||
>
|
>
|
||||||
<div
|
|
||||||
class="save-login inline-menu-list-message"
|
|
||||||
/>
|
|
||||||
<div
|
<div
|
||||||
class="inline-menu-list-button-container"
|
class="inline-menu-list-button-container"
|
||||||
>
|
>
|
||||||
<button
|
<button
|
||||||
aria-label=""
|
aria-label=", opensInANewWindow"
|
||||||
class="add-new-item-button inline-menu-list-button inline-menu-list-action"
|
class="save-login inline-menu-list-button inline-menu-list-action"
|
||||||
id="new-item-button"
|
|
||||||
tabindex="-1"
|
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>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|||||||
@@ -1089,12 +1089,12 @@ describe("AutofillInlineMenuList", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe("displaying the save login view", () => {
|
describe("displaying the save login view", () => {
|
||||||
let buildSaveLoginInlineMenuListSpy: jest.SpyInstance;
|
let buildSaveLoginInlineMenuSpy: jest.SpyInstance;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
buildSaveLoginInlineMenuListSpy = jest.spyOn(
|
buildSaveLoginInlineMenuSpy = jest.spyOn(
|
||||||
autofillInlineMenuList as any,
|
autofillInlineMenuList as any,
|
||||||
"buildSaveLoginInlineMenuList",
|
"buildSaveLoginInlineMenu",
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -1108,7 +1108,7 @@ describe("AutofillInlineMenuList", () => {
|
|||||||
|
|
||||||
postWindowMessage({ command: "showSaveLoginInlineMenuList" });
|
postWindowMessage({ command: "showSaveLoginInlineMenuList" });
|
||||||
|
|
||||||
expect(buildSaveLoginInlineMenuListSpy).not.toHaveBeenCalled();
|
expect(buildSaveLoginInlineMenuSpy).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("builds the save login item view", async () => {
|
it("builds the save login item view", async () => {
|
||||||
@@ -1117,7 +1117,7 @@ describe("AutofillInlineMenuList", () => {
|
|||||||
|
|
||||||
postWindowMessage({ command: "showSaveLoginInlineMenuList" });
|
postWindowMessage({ command: "showSaveLoginInlineMenuList" });
|
||||||
|
|
||||||
expect(buildSaveLoginInlineMenuListSpy).toHaveBeenCalled();
|
expect(buildSaveLoginInlineMenuSpy).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,8 @@
|
|||||||
import "@webcomponents/custom-elements";
|
import "@webcomponents/custom-elements";
|
||||||
import "lit/polyfill-support.js";
|
import "lit/polyfill-support.js";
|
||||||
|
|
||||||
|
import { FocusableElement } from "tabbable";
|
||||||
|
|
||||||
import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status";
|
import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status";
|
||||||
import { EVENTS, UPDATE_PASSKEYS_HEADINGS_ON_SCROLL } from "@bitwarden/common/autofill/constants";
|
import { EVENTS, UPDATE_PASSKEYS_HEADINGS_ON_SCROLL } from "@bitwarden/common/autofill/constants";
|
||||||
import { CipherRepromptType, CipherType } from "@bitwarden/common/vault/enums";
|
import { CipherRepromptType, CipherType } from "@bitwarden/common/vault/enums";
|
||||||
@@ -117,7 +119,7 @@ export class AutofillInlineMenuList extends AutofillInlineMenuPageElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (showSaveLoginMenu) {
|
if (showSaveLoginMenu) {
|
||||||
this.buildSaveLoginInlineMenuList();
|
this.buildSaveLoginInlineMenu();
|
||||||
return;
|
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.
|
* Builds the inline menu list as a prompt that asks the user if they'd like to save the login data.
|
||||||
*/
|
*/
|
||||||
private buildSaveLoginInlineMenuList() {
|
private buildSaveLoginInlineMenu() {
|
||||||
const saveLoginMessage = globalThis.document.createElement("div");
|
const saveLoginButton = globalThis.document.createElement("button");
|
||||||
saveLoginMessage.classList.add("save-login", "inline-menu-list-message");
|
saveLoginButton.classList.add(
|
||||||
saveLoginMessage.textContent = this.getTranslation("saveLoginToBitwarden");
|
"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.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.
|
* Handles the show save login inline menu list message that is triggered from the background script.
|
||||||
*/
|
*/
|
||||||
private handleShowSaveLoginInlineMenuList() {
|
private handleShowSaveLoginInlineMenuList() {
|
||||||
if (this.authStatus === AuthenticationStatus.Unlocked) {
|
if (this.authStatus === AuthenticationStatus.Unlocked) {
|
||||||
this.resetInlineMenuContainer();
|
this.resetInlineMenuContainer();
|
||||||
this.buildSaveLoginInlineMenuList();
|
this.buildSaveLoginInlineMenu();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -521,7 +551,7 @@ export class AutofillInlineMenuList extends AutofillInlineMenuPageElement {
|
|||||||
this.newItemButtonElement.textContent = this.getNewItemButtonText(showLogin);
|
this.newItemButtonElement.textContent = this.getNewItemButtonText(showLogin);
|
||||||
this.newItemButtonElement.setAttribute("aria-label", this.getNewItemAriaLabel(showLogin));
|
this.newItemButtonElement.setAttribute("aria-label", this.getNewItemAriaLabel(showLogin));
|
||||||
this.newItemButtonElement.prepend(buildSvgDomElement(plusIcon));
|
this.newItemButtonElement.prepend(buildSvgDomElement(plusIcon));
|
||||||
this.newItemButtonElement.addEventListener(EVENTS.CLICK, this.handeNewItemButtonClick);
|
this.newItemButtonElement.addEventListener(EVENTS.CLICK, this.handleNewLoginVaultItemAction);
|
||||||
|
|
||||||
return this.buildButtonContainer(this.newItemButtonElement);
|
return this.buildButtonContainer(this.newItemButtonElement);
|
||||||
}
|
}
|
||||||
@@ -581,7 +611,7 @@ export class AutofillInlineMenuList extends AutofillInlineMenuPageElement {
|
|||||||
* Handles the click event for the new item button.
|
* Handles the click event for the new item button.
|
||||||
* Sends a message to the parent window to add a new vault item.
|
* Sends a message to the parent window to add a new vault item.
|
||||||
*/
|
*/
|
||||||
private handeNewItemButtonClick = () => {
|
private handleNewLoginVaultItemAction = () => {
|
||||||
let addNewCipherType = this.inlineMenuFillType;
|
let addNewCipherType = this.inlineMenuFillType;
|
||||||
|
|
||||||
if (this.showInlineMenuAccountCreation) {
|
if (this.showInlineMenuAccountCreation) {
|
||||||
|
|||||||
@@ -45,6 +45,14 @@ body * {
|
|||||||
&.no-items,
|
&.no-items,
|
||||||
&.save-login {
|
&.save-login {
|
||||||
font-size: 1.6rem;
|
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