1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-17 16:53:34 +00:00

[PM-5189] Reworking project structure to ensure we can better differentiate the inline menu feature from other features

This commit is contained in:
Cesar Gonzalez
2024-05-03 14:30:00 -05:00
parent 4038939f46
commit c75004bbd2
11 changed files with 219 additions and 1467 deletions

View File

@@ -9,13 +9,3 @@ exports[`AutofillInlineMenuIframeService initMenuIframe sets up the iframe's att
title="title"
/>
`;
exports[`AutofillOverlayIframeService initMenuIframe sets up the iframe's attributes 1`] = `
<iframe
allowtransparency="true"
src="chrome-extension://id/overlay/menu.html"
style="all: initial !important; position: fixed !important; display: block !important; z-index: 2147483647 !important; line-height: 0 !important; overflow: hidden !important; transition: opacity 125ms ease-out 0s !important; visibility: visible !important; clip-path: none !important; pointer-events: auto !important; margin: 0px !important; padding: 0px !important; color-scheme: normal !important; opacity: 0 !important; height: 0px;"
tabindex="-1"
title="title"
/>
`;

View File

@@ -1,11 +0,0 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`AutofillOverlayIframeService initMenuIframe sets up the iframe's attributes 1`] = `
<iframe
allowtransparency="true"
src="chrome-extension://id/overlay/menu.html"
style="all: initial !important; position: fixed !important; display: block !important; z-index: 2147483647 !important; line-height: 0 !important; overflow: hidden !important; transition: opacity 125ms ease-out 0s !important; visibility: visible !important; clip-path: none !important; pointer-events: auto !important; margin: 0px !important; padding: 0px !important; color-scheme: normal !important; opacity: 0 !important; height: 0px;"
tabindex="-1"
title="title"
/>
`;

View File

@@ -81,85 +81,3 @@ exports[`AutofillInlineMenuButton initAutofillInlineMenuButton creates the butto
</svg>
</button>
`;
exports[`AutofillOverlayButton initAutofillInlineMenuButton creates the button element with the locked icon when the user's auth status is not Unlocked 1`] = `
<button
aria-label="toggleBitwardenVaultOverlay"
class="inline-menu-button"
tabindex="-1"
type="button"
>
<svg
aria-hidden="true"
class="inline-menu-button-svg-icon logo-locked-icon"
fill="none"
height="16"
viewBox="0 0 16 16"
width="16"
xmlns="http://www.w3.org/2000/svg"
>
<g
clip-path="url(#a)"
>
<path
d="M12.66.175A.566.566 0 0 0 12.25 0H1.75a.559.559 0 0 0-.409.175.561.561 0 0 0-.175.41v7c.002.532.105 1.06.305 1.554.189.488.444.948.756 1.368.322.42.682.81 1.076 1.163.365.335.75.649 1.152.939.35.248.718.483 1.103.706.385.222.656.372.815.45.16.08.29.141.386.182A.53.53 0 0 0 7 14a.509.509 0 0 0 .238-.055c.098-.043.225-.104.387-.182.162-.079.438-.23.816-.45.378-.222.75-.459 1.102-.707.403-.29.788-.604 1.154-.939a8.435 8.435 0 0 0 1.076-1.163c.312-.42.567-.88.757-1.367a4.19 4.19 0 0 0 .304-1.555v-7a.55.55 0 0 0-.174-.407Z"
fill="#175DDC"
/>
<path
d="M7 12.365s4.306-2.18 4.306-4.717V1.5H7v10.865Z"
fill="#fff"
/>
<circle
cx="12.889"
cy="12.889"
fill="#F8F9FA"
r="4.889"
/>
<path
d="M11.26 11.717h2.37v-.848c0-.313-.116-.58-.348-.8a1.17 1.17 0 0 0-.838-.332c-.327 0-.606.11-.838.332a1.066 1.066 0 0 0-.347.8v.848Zm3.851.424v2.546a.4.4 0 0 1-.13.3.44.44 0 0 1-.314.124h-4.445a.44.44 0 0 1-.315-.124.4.4 0 0 1-.13-.3V12.14a.4.4 0 0 1 .13-.3.44.44 0 0 1 .315-.124h.148v-.848c0-.542.204-1.008.612-1.397a2.044 2.044 0 0 1 1.462-.583c.568 0 1.056.194 1.463.583.408.39.611.855.611 1.397v.848h.149a.44.44 0 0 1 .315.124.4.4 0 0 1 .13.3Z"
fill="#555"
/>
</g>
<defs>
<clippath
id="a"
>
<rect
fill="#fff"
height="16"
rx="2"
width="16"
/>
</clippath>
</defs>
</svg>
</button>
`;
exports[`AutofillOverlayButton initAutofillInlineMenuButton creates the button element with the normal icon when the user's auth status is Unlocked 1`] = `
<button
aria-label="toggleBitwardenVaultOverlay"
class="inline-menu-button"
tabindex="-1"
type="button"
>
<svg
aria-hidden="true"
class="inline-menu-button-svg-icon logo-icon"
fill="none"
height="14"
viewBox="0 0 14 14"
width="14"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M12.66.175A.566.566 0 0 0 12.25 0H1.75a.559.559 0 0 0-.409.175.561.561 0 0 0-.175.41v7c.002.532.105 1.06.305 1.554.189.488.444.948.756 1.368.322.42.682.81 1.076 1.163.365.335.75.649 1.152.939.35.248.718.483 1.103.706.385.222.656.372.815.45.16.08.29.141.386.182A.53.53 0 0 0 7 14a.509.509 0 0 0 .238-.055c.098-.043.225-.104.387-.182.162-.079.438-.23.816-.45.378-.222.75-.459 1.102-.707.403-.29.788-.604 1.154-.939a8.435 8.435 0 0 0 1.076-1.163c.312-.42.567-.88.757-1.367a4.19 4.19 0 0 0 .304-1.555v-7a.55.55 0 0 0-.174-.407Z"
fill="#175DDC"
/>
<path
d="M7 12.365s4.306-2.18 4.306-4.717V1.5H7v10.865Z"
fill="#fff"
/>
</svg>
</button>
`;

View File

@@ -1,83 +0,0 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`AutofillOverlayButton initAutofillOverlayButton creates the button element with the locked icon when the user's auth status is not Unlocked 1`] = `
<button
aria-label="toggleBitwardenVaultOverlay"
class="inline-menu-button"
tabindex="-1"
type="button"
>
<svg
aria-hidden="true"
class="inline-menu-button-svg-icon logo-locked-icon"
fill="none"
height="16"
viewBox="0 0 16 16"
width="16"
xmlns="http://www.w3.org/2000/svg"
>
<g
clip-path="url(#a)"
>
<path
d="M12.66.175A.566.566 0 0 0 12.25 0H1.75a.559.559 0 0 0-.409.175.561.561 0 0 0-.175.41v7c.002.532.105 1.06.305 1.554.189.488.444.948.756 1.368.322.42.682.81 1.076 1.163.365.335.75.649 1.152.939.35.248.718.483 1.103.706.385.222.656.372.815.45.16.08.29.141.386.182A.53.53 0 0 0 7 14a.509.509 0 0 0 .238-.055c.098-.043.225-.104.387-.182.162-.079.438-.23.816-.45.378-.222.75-.459 1.102-.707.403-.29.788-.604 1.154-.939a8.435 8.435 0 0 0 1.076-1.163c.312-.42.567-.88.757-1.367a4.19 4.19 0 0 0 .304-1.555v-7a.55.55 0 0 0-.174-.407Z"
fill="#175DDC"
/>
<path
d="M7 12.365s4.306-2.18 4.306-4.717V1.5H7v10.865Z"
fill="#fff"
/>
<circle
cx="12.889"
cy="12.889"
fill="#F8F9FA"
r="4.889"
/>
<path
d="M11.26 11.717h2.37v-.848c0-.313-.116-.58-.348-.8a1.17 1.17 0 0 0-.838-.332c-.327 0-.606.11-.838.332a1.066 1.066 0 0 0-.347.8v.848Zm3.851.424v2.546a.4.4 0 0 1-.13.3.44.44 0 0 1-.314.124h-4.445a.44.44 0 0 1-.315-.124.4.4 0 0 1-.13-.3V12.14a.4.4 0 0 1 .13-.3.44.44 0 0 1 .315-.124h.148v-.848c0-.542.204-1.008.612-1.397a2.044 2.044 0 0 1 1.462-.583c.568 0 1.056.194 1.463.583.408.39.611.855.611 1.397v.848h.149a.44.44 0 0 1 .315.124.4.4 0 0 1 .13.3Z"
fill="#555"
/>
</g>
<defs>
<clippath
id="a"
>
<rect
fill="#fff"
height="16"
rx="2"
width="16"
/>
</clippath>
</defs>
</svg>
</button>
`;
exports[`AutofillOverlayButton initAutofillOverlayButton creates the button element with the normal icon when the user's auth status is Unlocked 1`] = `
<button
aria-label="toggleBitwardenVaultOverlay"
class="inline-menu-button"
tabindex="-1"
type="button"
>
<svg
aria-hidden="true"
class="inline-menu-button-svg-icon logo-icon"
fill="none"
height="14"
viewBox="0 0 14 14"
width="14"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M12.66.175A.566.566 0 0 0 12.25 0H1.75a.559.559 0 0 0-.409.175.561.561 0 0 0-.175.41v7c.002.532.105 1.06.305 1.554.189.488.444.948.756 1.368.322.42.682.81 1.076 1.163.365.335.75.649 1.152.939.35.248.718.483 1.103.706.385.222.656.372.815.45.16.08.29.141.386.182A.53.53 0 0 0 7 14a.509.509 0 0 0 .238-.055c.098-.043.225-.104.387-.182.162-.079.438-.23.816-.45.378-.222.75-.459 1.102-.707.403-.29.788-.604 1.154-.939a8.435 8.435 0 0 0 1.076-1.163c.312-.42.567-.88.757-1.367a4.19 4.19 0 0 0 .304-1.555v-7a.55.55 0 0 0-.174-.407Z"
fill="#175DDC"
/>
<path
d="M7 12.365s4.306-2.18 4.306-4.717V1.5H7v10.865Z"
fill="#fff"
/>
</svg>
</button>
`;

View File

@@ -41,7 +41,7 @@ class AutofillInlineMenuButton extends AutofillInlineMenuPageElement {
}
/**
* Initializes the overlay button. Facilitates ensuring that the page
* Initializes the inline menu button. Facilitates ensuring that the page
* is set up with the expected styles and translations.
*
* @param authStatus - The authentication status of the user
@@ -114,7 +114,7 @@ class AutofillInlineMenuButton extends AutofillInlineMenuPageElement {
/**
* Checks if the button is focused. If it is not, then it posts a message
* to the parent window indicating that the overlay should be closed.
* to the parent window indicating that the inline menu should be closed.
*/
private checkButtonFocused() {
if (globalThis.document.hasFocus()) {

View File

@@ -1,15 +1,66 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`AutofillOverlayList initAutofillInlineMenuList the list of ciphers for an authenticated user creates the view for a list of ciphers 1`] = `
exports[`AutofillInlineMenuList initAutofillInlineMenuList the inline menu with an empty list of ciphers creates the views for the no results inline menu 1`] = `
<div
class="overlay-list-container theme_light"
class="inline-menu-list-container theme_light"
>
<div
class="no-items inline-menu-list-message"
>
noItemsToShow
</div>
<div
class="inline-menu-list-button-container"
>
<button
aria-label="addNewVaultItem, opensInANewWindow"
class="add-new-item-button inline-menu-list-button"
id="new-item-button"
tabindex="-1"
>
<svg
aria-hidden="true"
fill="none"
height="17"
viewBox="0 0 16 17"
width="16"
xmlns="http://www.w3.org/2000/svg"
>
<g
clip-path="url(#a)"
>
<path
d="M15.222 7.914H8.963a.471.471 0 0 1-.34-.147.512.512 0 0 1-.142-.353V.99c0-.133-.05-.26-.14-.354a.471.471 0 0 0-.68 0 .51.51 0 0 0-.142.354v6.424c0 .132-.051.26-.142.353a.474.474 0 0 1-.34.147H.777a.47.47 0 0 0-.34.146.5.5 0 0 0-.14.354.522.522 0 0 0 .14.353.48.48 0 0 0 .34.147h6.26c.128 0 .25.052.34.146.09.094.142.221.142.354v6.576c0 .132.05.26.14.353a.471.471 0 0 0 .68 0 .512.512 0 0 0 .142-.353V9.414c0-.133.051-.26.142-.354a.474.474 0 0 1 .34-.146h6.26c.127 0 .25-.053.34-.147a.511.511 0 0 0 0-.707.472.472 0 0 0-.34-.146Z"
fill="#175DDC"
/>
</g>
<defs>
<clippath
id="a"
>
<path
d="M0 .49h16v16H0z"
fill="#fff"
/>
</clippath>
</defs>
</svg>
newItem
</button>
</div>
</div>
`;
exports[`AutofillInlineMenuList initAutofillInlineMenuList the list of ciphers for an authenticated user creates the view for a list of ciphers 1`] = `
<div
class="inline-menu-list-container theme_light"
>
<ul
class="overlay-actions-list"
class="inline-menu-list-actions"
role="list"
>
<li
class="overlay-actions-list-item"
class="inline-menu-list-actions-item"
role="listitem"
>
<div
@@ -79,7 +130,7 @@ exports[`AutofillOverlayList initAutofillInlineMenuList the list of ciphers for
</div>
</li>
<li
class="overlay-actions-list-item"
class="inline-menu-list-actions-item"
role="listitem"
>
<div
@@ -148,7 +199,7 @@ exports[`AutofillOverlayList initAutofillInlineMenuList the list of ciphers for
</div>
</li>
<li
class="overlay-actions-list-item"
class="inline-menu-list-actions-item"
role="listitem"
>
<div
@@ -204,7 +255,7 @@ exports[`AutofillOverlayList initAutofillInlineMenuList the list of ciphers for
</div>
</li>
<li
class="overlay-actions-list-item"
class="inline-menu-list-actions-item"
role="listitem"
>
<div
@@ -289,7 +340,7 @@ exports[`AutofillOverlayList initAutofillInlineMenuList the list of ciphers for
</div>
</li>
<li
class="overlay-actions-list-item"
class="inline-menu-list-actions-item"
role="listitem"
>
<div
@@ -359,7 +410,7 @@ exports[`AutofillOverlayList initAutofillInlineMenuList the list of ciphers for
</div>
</li>
<li
class="overlay-actions-list-item"
class="inline-menu-list-actions-item"
role="listitem"
>
<div
@@ -432,22 +483,22 @@ exports[`AutofillOverlayList initAutofillInlineMenuList the list of ciphers for
</div>
`;
exports[`AutofillOverlayList initAutofillInlineMenuList the locked overlay for an unauthenticated user creates the views for the locked overlay 1`] = `
exports[`AutofillInlineMenuList initAutofillInlineMenuList the locked inline menu for an unauthenticated user creates the views for the locked inline menu 1`] = `
<div
class="overlay-list-container theme_light"
class="inline-menu-list-container theme_light"
>
<div
class="locked-overlay overlay-list-message"
id="locked-overlay-description"
class="locked-inline-menu inline-menu-list-message"
id="locked-inline-menu-description"
>
unlockYourAccount
</div>
<div
class="overlay-list-button-container"
class="inline-menu-list-button-container"
>
<button
aria-label="unlockAccount, opensInANewWindow"
class="unlock-button overlay-list-button"
class="unlock-button inline-menu-list-button"
id="unlock-button"
tabindex="-1"
>
@@ -483,54 +534,3 @@ exports[`AutofillOverlayList initAutofillInlineMenuList the locked overlay for a
</div>
</div>
`;
exports[`AutofillOverlayList initAutofillInlineMenuList the overlay with an empty list of ciphers creates the views for the no results overlay 1`] = `
<div
class="overlay-list-container theme_light"
>
<div
class="no-items overlay-list-message"
>
noItemsToShow
</div>
<div
class="overlay-list-button-container"
>
<button
aria-label="addNewVaultItem, opensInANewWindow"
class="add-new-item-button overlay-list-button"
id="new-item-button"
tabindex="-1"
>
<svg
aria-hidden="true"
fill="none"
height="17"
viewBox="0 0 16 17"
width="16"
xmlns="http://www.w3.org/2000/svg"
>
<g
clip-path="url(#a)"
>
<path
d="M15.222 7.914H8.963a.471.471 0 0 1-.34-.147.512.512 0 0 1-.142-.353V.99c0-.133-.05-.26-.14-.354a.471.471 0 0 0-.68 0 .51.51 0 0 0-.142.354v6.424c0 .132-.051.26-.142.353a.474.474 0 0 1-.34.147H.777a.47.47 0 0 0-.34.146.5.5 0 0 0-.14.354.522.522 0 0 0 .14.353.48.48 0 0 0 .34.147h6.26c.128 0 .25.052.34.146.09.094.142.221.142.354v6.576c0 .132.05.26.14.353a.471.471 0 0 0 .68 0 .512.512 0 0 0 .142-.353V9.414c0-.133.051-.26.142-.354a.474.474 0 0 1 .34-.146h6.26c.127 0 .25-.053.34-.147a.511.511 0 0 0 0-.707.472.472 0 0 0-.34-.146Z"
fill="#175DDC"
/>
</g>
<defs>
<clippath
id="a"
>
<path
d="M0 .49h16v16H0z"
fill="#fff"
/>
</clippath>
</defs>
</svg>
newItem
</button>
</div>
</div>
`;

View File

@@ -2,12 +2,12 @@ import { mock } from "jest-mock-extended";
import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status";
import { createInitAutofillOverlayListMessageMock } from "../../../../spec/autofill-mocks";
import { createInitAutofillInlineMenuListMessageMock } from "../../../../spec/autofill-mocks";
import { flushPromises, postWindowMessage } from "../../../../spec/testing-utils";
import { AutofillInlineMenuList } from "./autofill-inline-menu-list";
describe("AutofillOverlayList", () => {
describe("AutofillInlineMenuList", () => {
globalThis.customElements.define("autofill-inline-menu-list", AutofillInlineMenuList);
global.ResizeObserver = jest.fn().mockImplementation(() => ({
observe: jest.fn(),
@@ -15,12 +15,12 @@ describe("AutofillOverlayList", () => {
disconnect: jest.fn(),
}));
let autofillOverlayList: AutofillInlineMenuList;
const portKey: string = "overlayListPortKey";
let autofillInlineMenuList: AutofillInlineMenuList;
const portKey: string = "inlineMenuListPortKey";
beforeEach(() => {
document.body.innerHTML = `<autofill-inline-menu-list></autofill-inline-menu-list>`;
autofillOverlayList = document.querySelector("autofill-inline-menu-list");
autofillInlineMenuList = document.querySelector("autofill-inline-menu-list");
jest.spyOn(globalThis.document, "createElement");
jest.spyOn(globalThis.parent, "postMessage");
});
@@ -30,10 +30,10 @@ describe("AutofillOverlayList", () => {
});
describe("initAutofillInlineMenuList", () => {
describe("the locked overlay for an unauthenticated user", () => {
describe("the locked inline menu for an unauthenticated user", () => {
beforeEach(() => {
postWindowMessage(
createInitAutofillOverlayListMessageMock({
createInitAutofillInlineMenuListMessageMock({
authStatus: AuthenticationStatus.Locked,
cipherList: [],
portKey,
@@ -41,13 +41,13 @@ describe("AutofillOverlayList", () => {
);
});
it("creates the views for the locked overlay", () => {
expect(autofillOverlayList["overlayListContainer"]).toMatchSnapshot();
it("creates the views for the locked inline menu", () => {
expect(autofillInlineMenuList["inlineMenuListContainer"]).toMatchSnapshot();
});
it("allows the user to unlock the vault", () => {
const unlockButton =
autofillOverlayList["overlayListContainer"].querySelector("#unlock-button");
autofillInlineMenuList["inlineMenuListContainer"].querySelector("#unlock-button");
unlockButton.dispatchEvent(new Event("click"));
@@ -58,10 +58,10 @@ describe("AutofillOverlayList", () => {
});
});
describe("the overlay with an empty list of ciphers", () => {
describe("the inline menu with an empty list of ciphers", () => {
beforeEach(() => {
postWindowMessage(
createInitAutofillOverlayListMessageMock({
createInitAutofillInlineMenuListMessageMock({
authStatus: AuthenticationStatus.Unlocked,
ciphers: [],
portKey,
@@ -69,13 +69,13 @@ describe("AutofillOverlayList", () => {
);
});
it("creates the views for the no results overlay", () => {
expect(autofillOverlayList["overlayListContainer"]).toMatchSnapshot();
it("creates the views for the no results inline menu", () => {
expect(autofillInlineMenuList["inlineMenuListContainer"]).toMatchSnapshot();
});
it("allows the user to add a vault item", () => {
const addVaultItemButton =
autofillOverlayList["overlayListContainer"].querySelector("#new-item-button");
autofillInlineMenuList["inlineMenuListContainer"].querySelector("#new-item-button");
addVaultItemButton.dispatchEvent(new Event("click"));
@@ -88,23 +88,23 @@ describe("AutofillOverlayList", () => {
describe("the list of ciphers for an authenticated user", () => {
beforeEach(() => {
postWindowMessage(createInitAutofillOverlayListMessageMock());
postWindowMessage(createInitAutofillInlineMenuListMessageMock());
});
it("creates the view for a list of ciphers", () => {
expect(autofillOverlayList["overlayListContainer"]).toMatchSnapshot();
expect(autofillInlineMenuList["inlineMenuListContainer"]).toMatchSnapshot();
});
it("loads ciphers on scroll one page at a time", () => {
jest.useFakeTimers();
const originalListOfElements =
autofillOverlayList["overlayListContainer"].querySelectorAll(".cipher-container");
autofillInlineMenuList["inlineMenuListContainer"].querySelectorAll(".cipher-container");
window.dispatchEvent(new Event("scroll"));
jest.runAllTimers();
const updatedListOfElements =
autofillOverlayList["overlayListContainer"].querySelectorAll(".cipher-container");
autofillInlineMenuList["inlineMenuListContainer"].querySelectorAll(".cipher-container");
expect(originalListOfElements.length).toBe(6);
expect(updatedListOfElements.length).toBe(8);
@@ -112,9 +112,9 @@ describe("AutofillOverlayList", () => {
it("debounces the ciphers scroll handler", () => {
jest.useFakeTimers();
autofillOverlayList["cipherListScrollDebounceTimeout"] = setTimeout(jest.fn, 0);
autofillInlineMenuList["cipherListScrollDebounceTimeout"] = setTimeout(jest.fn, 0);
const handleDebouncedScrollEventSpy = jest.spyOn(
autofillOverlayList as any,
autofillInlineMenuList as any,
"handleDebouncedScrollEvent",
);
@@ -130,12 +130,12 @@ describe("AutofillOverlayList", () => {
describe("fill cipher button event listeners", () => {
beforeEach(() => {
postWindowMessage(createInitAutofillOverlayListMessageMock({ portKey }));
postWindowMessage(createInitAutofillInlineMenuListMessageMock({ portKey }));
});
it("allows the user to fill a cipher on click", () => {
const fillCipherButton =
autofillOverlayList["overlayListContainer"].querySelector(".fill-cipher-button");
autofillInlineMenuList["inlineMenuListContainer"].querySelector(".fill-cipher-button");
fillCipherButton.dispatchEvent(new Event("click"));
@@ -147,7 +147,9 @@ describe("AutofillOverlayList", () => {
it("allows the user to move keyboard focus to the next cipher element on ArrowDown", () => {
const fillCipherElements =
autofillOverlayList["overlayListContainer"].querySelectorAll(".fill-cipher-button");
autofillInlineMenuList["inlineMenuListContainer"].querySelectorAll(
".fill-cipher-button",
);
const firstFillCipherElement = fillCipherElements[0];
const secondFillCipherElement = fillCipherElements[1];
jest.spyOn(secondFillCipherElement as HTMLElement, "focus");
@@ -159,7 +161,9 @@ describe("AutofillOverlayList", () => {
it("directs focus to the first item in the cipher list if no cipher is present after the current one when pressing ArrowDown", () => {
const fillCipherElements =
autofillOverlayList["overlayListContainer"].querySelectorAll(".fill-cipher-button");
autofillInlineMenuList["inlineMenuListContainer"].querySelectorAll(
".fill-cipher-button",
);
const lastFillCipherElement = fillCipherElements[fillCipherElements.length - 1];
const firstFillCipherElement = fillCipherElements[0];
jest.spyOn(firstFillCipherElement as HTMLElement, "focus");
@@ -171,7 +175,9 @@ describe("AutofillOverlayList", () => {
it("allows the user to move keyboard focus to the previous cipher element on ArrowUp", () => {
const fillCipherElements =
autofillOverlayList["overlayListContainer"].querySelectorAll(".fill-cipher-button");
autofillInlineMenuList["inlineMenuListContainer"].querySelectorAll(
".fill-cipher-button",
);
const firstFillCipherElement = fillCipherElements[0];
const secondFillCipherElement = fillCipherElements[1];
jest.spyOn(firstFillCipherElement as HTMLElement, "focus");
@@ -183,7 +189,9 @@ describe("AutofillOverlayList", () => {
it("directs focus to the last item in the cipher list if no cipher is present before the current one when pressing ArrowUp", () => {
const fillCipherElements =
autofillOverlayList["overlayListContainer"].querySelectorAll(".fill-cipher-button");
autofillInlineMenuList["inlineMenuListContainer"].querySelectorAll(
".fill-cipher-button",
);
const firstFillCipherElement = fillCipherElements[0];
const lastFillCipherElement = fillCipherElements[fillCipherElements.length - 1];
jest.spyOn(lastFillCipherElement as HTMLElement, "focus");
@@ -195,7 +203,7 @@ describe("AutofillOverlayList", () => {
it("allows the user to move keyboard focus to the view cipher button on ArrowRight", () => {
const cipherContainerElement =
autofillOverlayList["overlayListContainer"].querySelector(".cipher-container");
autofillInlineMenuList["inlineMenuListContainer"].querySelector(".cipher-container");
const fillCipherElement = cipherContainerElement.querySelector(".fill-cipher-button");
const viewCipherButton = cipherContainerElement.querySelector(".view-cipher-button");
jest.spyOn(viewCipherButton as HTMLElement, "focus");
@@ -207,7 +215,7 @@ describe("AutofillOverlayList", () => {
it("ignores keyup events that do not include ArrowUp, ArrowDown, or ArrowRight", () => {
const fillCipherElement =
autofillOverlayList["overlayListContainer"].querySelector(".fill-cipher-button");
autofillInlineMenuList["inlineMenuListContainer"].querySelector(".fill-cipher-button");
jest.spyOn(fillCipherElement as HTMLElement, "focus");
fillCipherElement.dispatchEvent(new KeyboardEvent("keyup", { code: "ArrowLeft" }));
@@ -218,12 +226,12 @@ describe("AutofillOverlayList", () => {
describe("view cipher button event listeners", () => {
beforeEach(() => {
postWindowMessage(createInitAutofillOverlayListMessageMock({ portKey }));
postWindowMessage(createInitAutofillInlineMenuListMessageMock({ portKey }));
});
it("allows the user to view a cipher on click", () => {
const viewCipherButton =
autofillOverlayList["overlayListContainer"].querySelector(".view-cipher-button");
autofillInlineMenuList["inlineMenuListContainer"].querySelector(".view-cipher-button");
viewCipherButton.dispatchEvent(new Event("click"));
@@ -235,7 +243,7 @@ describe("AutofillOverlayList", () => {
it("allows the user to move keyboard focus to the current cipher element on ArrowLeft", () => {
const cipherContainerElement =
autofillOverlayList["overlayListContainer"].querySelector(".cipher-container");
autofillInlineMenuList["inlineMenuListContainer"].querySelector(".cipher-container");
const fillCipherButton = cipherContainerElement.querySelector(".fill-cipher-button");
const viewCipherButton = cipherContainerElement.querySelector(".view-cipher-button");
jest.spyOn(fillCipherButton as HTMLElement, "focus");
@@ -247,7 +255,7 @@ describe("AutofillOverlayList", () => {
it("allows the user to move keyboard to the next cipher element on ArrowDown", () => {
const cipherContainerElements =
autofillOverlayList["overlayListContainer"].querySelectorAll(".cipher-container");
autofillInlineMenuList["inlineMenuListContainer"].querySelectorAll(".cipher-container");
const viewCipherButton = cipherContainerElements[0].querySelector(".view-cipher-button");
const secondFillCipherButton =
cipherContainerElements[1].querySelector(".fill-cipher-button");
@@ -260,7 +268,7 @@ describe("AutofillOverlayList", () => {
it("allows the user to move keyboard focus to the previous cipher element on ArrowUp", () => {
const cipherContainerElements =
autofillOverlayList["overlayListContainer"].querySelectorAll(".cipher-container");
autofillInlineMenuList["inlineMenuListContainer"].querySelectorAll(".cipher-container");
const viewCipherButton = cipherContainerElements[1].querySelector(".view-cipher-button");
const firstFillCipherButton =
cipherContainerElements[0].querySelector(".fill-cipher-button");
@@ -273,7 +281,7 @@ describe("AutofillOverlayList", () => {
it("ignores keyup events that do not include ArrowUp, ArrowDown, or ArrowRight", () => {
const viewCipherButton =
autofillOverlayList["overlayListContainer"].querySelector(".view-cipher-button");
autofillInlineMenuList["inlineMenuListContainer"].querySelector(".view-cipher-button");
jest.spyOn(viewCipherButton as HTMLElement, "focus");
viewCipherButton.dispatchEvent(new KeyboardEvent("keyup", { code: "ArrowRight" }));
@@ -286,10 +294,10 @@ describe("AutofillOverlayList", () => {
describe("global event listener handlers", () => {
beforeEach(() => {
postWindowMessage(createInitAutofillOverlayListMessageMock({ portKey }));
postWindowMessage(createInitAutofillInlineMenuListMessageMock({ portKey }));
});
it("does not post a `checkAutofillInlineMenuButtonFocused` message to the parent if the overlay is currently focused", () => {
it("does not post a `checkAutofillInlineMenuButtonFocused` message to the parent if the inline menu is currently focused", () => {
jest.spyOn(globalThis.document, "hasFocus").mockReturnValue(true);
postWindowMessage({ command: "checkAutofillInlineMenuListFocused" });
@@ -297,7 +305,7 @@ describe("AutofillOverlayList", () => {
expect(globalThis.parent.postMessage).not.toHaveBeenCalled();
});
it("posts a `checkAutofillInlineMenuButtonFocused` message to the parent if the overlay is not currently focused", () => {
it("posts a `checkAutofillInlineMenuButtonFocused` message to the parent if the inline menu is not currently focused", () => {
jest.spyOn(globalThis.document, "hasFocus").mockReturnValue(false);
postWindowMessage({ command: "checkAutofillInlineMenuListFocused" });
@@ -309,43 +317,43 @@ describe("AutofillOverlayList", () => {
});
it("updates the list of ciphers", () => {
postWindowMessage(createInitAutofillOverlayListMessageMock());
const updateCiphersSpy = jest.spyOn(autofillOverlayList as any, "updateListItems");
postWindowMessage(createInitAutofillInlineMenuListMessageMock());
const updateCiphersSpy = jest.spyOn(autofillInlineMenuList as any, "updateListItems");
postWindowMessage({ command: "updateAutofillInlineMenuListCiphers" });
expect(updateCiphersSpy).toHaveBeenCalled();
});
describe("directing user focus into the overlay list", () => {
describe("directing user focus into the inline menu list", () => {
it("sets ARIA attributes that define the list as a `dialog` to screen reader users", () => {
postWindowMessage(
createInitAutofillOverlayListMessageMock({
createInitAutofillInlineMenuListMessageMock({
authStatus: AuthenticationStatus.Locked,
cipherList: [],
}),
);
const overlayContainerSetAttributeSpy = jest.spyOn(
autofillOverlayList["overlayListContainer"],
const inlineMenuContainerSetAttributeSpy = jest.spyOn(
autofillInlineMenuList["inlineMenuListContainer"],
"setAttribute",
);
postWindowMessage({ command: "focusInlineMenuList" });
expect(overlayContainerSetAttributeSpy).toHaveBeenCalledWith("role", "dialog");
expect(overlayContainerSetAttributeSpy).toHaveBeenCalledWith("aria-modal", "true");
expect(inlineMenuContainerSetAttributeSpy).toHaveBeenCalledWith("role", "dialog");
expect(inlineMenuContainerSetAttributeSpy).toHaveBeenCalledWith("aria-modal", "true");
});
it("focuses the unlock button element if the user is not authenticated", async () => {
postWindowMessage(
createInitAutofillOverlayListMessageMock({
createInitAutofillInlineMenuListMessageMock({
authStatus: AuthenticationStatus.Locked,
cipherList: [],
}),
);
await flushPromises();
const unlockButton =
autofillOverlayList["overlayListContainer"].querySelector("#unlock-button");
autofillInlineMenuList["inlineMenuListContainer"].querySelector("#unlock-button");
jest.spyOn(unlockButton as HTMLElement, "focus");
postWindowMessage({ command: "focusInlineMenuList" });
@@ -354,10 +362,10 @@ describe("AutofillOverlayList", () => {
});
it("focuses the new item button element if the cipher list is empty", async () => {
postWindowMessage(createInitAutofillOverlayListMessageMock({ ciphers: [] }));
postWindowMessage(createInitAutofillInlineMenuListMessageMock({ ciphers: [] }));
await flushPromises();
const newItemButton =
autofillOverlayList["overlayListContainer"].querySelector("#new-item-button");
autofillInlineMenuList["inlineMenuListContainer"].querySelector("#new-item-button");
jest.spyOn(newItemButton as HTMLElement, "focus");
postWindowMessage({ command: "focusInlineMenuList" });
@@ -366,9 +374,9 @@ describe("AutofillOverlayList", () => {
});
it("focuses the first cipher button element if the cipher list is populated", () => {
postWindowMessage(createInitAutofillOverlayListMessageMock());
postWindowMessage(createInitAutofillInlineMenuListMessageMock());
const firstCipherItem =
autofillOverlayList["overlayListContainer"].querySelector(".fill-cipher-button");
autofillInlineMenuList["inlineMenuListContainer"].querySelector(".fill-cipher-button");
jest.spyOn(firstCipherItem as HTMLElement, "focus");
postWindowMessage({ command: "focusInlineMenuList" });
@@ -378,8 +386,8 @@ describe("AutofillOverlayList", () => {
});
describe("blur event", () => {
it("posts a message to the parent window indicating that the overlay has lost focus", () => {
postWindowMessage(createInitAutofillOverlayListMessageMock({ portKey }));
it("posts a message to the parent window indicating that the inline menu has lost focus", () => {
postWindowMessage(createInitAutofillInlineMenuListMessageMock({ portKey }));
globalThis.dispatchEvent(new Event("blur"));
@@ -392,7 +400,7 @@ describe("AutofillOverlayList", () => {
describe("keydown event", () => {
beforeEach(() => {
postWindowMessage(createInitAutofillOverlayListMessageMock({ portKey }));
postWindowMessage(createInitAutofillInlineMenuListMessageMock({ portKey }));
});
it("skips redirecting keyboard focus when a KeyDown event triggers and the key is not a `Tab` or `Escape` key", () => {
@@ -401,7 +409,7 @@ describe("AutofillOverlayList", () => {
expect(globalThis.parent.postMessage).not.toHaveBeenCalled();
});
it("redirects the overlay focus out to the previous element on KeyDown of the `Tab+Shift` keys", () => {
it("redirects the inline menu focus out to the previous element on KeyDown of the `Tab+Shift` keys", () => {
globalThis.document.dispatchEvent(
new KeyboardEvent("keydown", { code: "Tab", shiftKey: true }),
);
@@ -412,7 +420,7 @@ describe("AutofillOverlayList", () => {
);
});
it("redirects the overlay focus out to the next element on KeyDown of the `Tab` key", () => {
it("redirects the inline menu focus out to the next element on KeyDown of the `Tab` key", () => {
globalThis.document.dispatchEvent(new KeyboardEvent("keydown", { code: "Tab" }));
expect(globalThis.parent.postMessage).toHaveBeenCalledWith(
@@ -421,7 +429,7 @@ describe("AutofillOverlayList", () => {
);
});
it("redirects the overlay focus out to the current element on KeyDown of the `Escape` key", () => {
it("redirects the inline menu focus out to the current element on KeyDown of the `Escape` key", () => {
globalThis.document.dispatchEvent(new KeyboardEvent("keydown", { code: "Escape" }));
expect(globalThis.parent.postMessage).toHaveBeenCalledWith(
@@ -434,10 +442,10 @@ describe("AutofillOverlayList", () => {
describe("handleResizeObserver", () => {
beforeEach(() => {
postWindowMessage(createInitAutofillOverlayListMessageMock({ portKey }));
postWindowMessage(createInitAutofillInlineMenuListMessageMock({ portKey }));
});
it("ignores resize entries whose target is not the overlay list", () => {
it("ignores resize entries whose target is not the inline menu list", () => {
const entries = [
{
target: mock<HTMLElement>(),
@@ -445,20 +453,20 @@ describe("AutofillOverlayList", () => {
},
];
autofillOverlayList["handleResizeObserver"](entries as unknown as ResizeObserverEntry[]);
autofillInlineMenuList["handleResizeObserver"](entries as unknown as ResizeObserverEntry[]);
expect(globalThis.parent.postMessage).not.toHaveBeenCalled();
});
it("posts a message to update the overlay list height if the list container is resized", () => {
it("posts a message to update the inline menu list height if the list container is resized", () => {
const entries = [
{
target: autofillOverlayList["overlayListContainer"],
target: autofillInlineMenuList["inlineMenuListContainer"],
contentRect: { height: 300 },
},
];
autofillOverlayList["handleResizeObserver"](entries as unknown as ResizeObserverEntry[]);
autofillInlineMenuList["handleResizeObserver"](entries as unknown as ResizeObserverEntry[]);
expect(globalThis.parent.postMessage).toHaveBeenCalledWith(
{ command: "updateAutofillInlineMenuListHeight", styles: { height: "300px" }, portKey },

View File

@@ -13,7 +13,7 @@ import {
import { AutofillInlineMenuPageElement } from "../shared/autofill-inline-menu-page-element";
export class AutofillInlineMenuList extends AutofillInlineMenuPageElement {
private overlayListContainer: HTMLDivElement;
private inlineMenuListContainer: HTMLDivElement;
private resizeObserver: ResizeObserver;
private eventHandlersMemo: { [key: string]: EventListener } = {};
private ciphers: OverlayCipherData[] = [];
@@ -22,28 +22,29 @@ export class AutofillInlineMenuList extends AutofillInlineMenuPageElement {
private cipherListScrollDebounceTimeout: number | NodeJS.Timeout;
private currentCipherIndex = 0;
private readonly showCiphersPerPage = 6;
private readonly overlayListWindowMessageHandlers: AutofillInlineMenuListWindowMessageHandlers = {
initAutofillInlineMenuList: ({ message }) => this.initAutofillInlineMenuList(message),
checkAutofillInlineMenuListFocused: () => this.checkInlineMenuListFocused(),
updateAutofillInlineMenuListCiphers: ({ message }) => this.updateListItems(message.ciphers),
focusInlineMenuList: () => this.focusInlineMenuList(),
};
private readonly inlineMenuListWindowMessageHandlers: AutofillInlineMenuListWindowMessageHandlers =
{
initAutofillInlineMenuList: ({ message }) => this.initAutofillInlineMenuList(message),
checkAutofillInlineMenuListFocused: () => this.checkInlineMenuListFocused(),
updateAutofillInlineMenuListCiphers: ({ message }) => this.updateListItems(message.ciphers),
focusInlineMenuList: () => this.focusInlineMenuList(),
};
constructor() {
super();
this.setupOverlayListGlobalListeners();
this.setupInlineMenuListGlobalListeners();
}
/**
* Initializes the overlay list and updates the list items with the passed ciphers.
* If the auth status is not `Unlocked`, the locked overlay is built.
* Initializes the inline menu list and updates the list items with the passed ciphers.
* If the auth status is not `Unlocked`, the locked inline menu is built.
*
* @param translations - The translations to use for the overlay list.
* @param styleSheetUrl - The URL of the stylesheet to use for the overlay list.
* @param theme - The theme to use for the overlay list.
* @param translations - The translations to use for the inline menu list.
* @param styleSheetUrl - The URL of the stylesheet to use for the inline menu list.
* @param theme - The theme to use for the inline menu list.
* @param authStatus - The current authentication status.
* @param ciphers - The ciphers to display in the overlay list.
* @param ciphers - The ciphers to display in the inline menu list.
* @param portKey - Background generated key that allows the port to communicate with the background.
*/
private async initAutofillInlineMenuList({
@@ -64,34 +65,34 @@ export class AutofillInlineMenuList extends AutofillInlineMenuPageElement {
const themeClass = `theme_${theme}`;
globalThis.document.documentElement.classList.add(themeClass);
this.overlayListContainer = globalThis.document.createElement("div");
this.overlayListContainer.classList.add("overlay-list-container", themeClass);
this.resizeObserver.observe(this.overlayListContainer);
this.inlineMenuListContainer = globalThis.document.createElement("div");
this.inlineMenuListContainer.classList.add("inline-menu-list-container", themeClass);
this.resizeObserver.observe(this.inlineMenuListContainer);
this.shadowDom.append(linkElement, this.overlayListContainer);
this.shadowDom.append(linkElement, this.inlineMenuListContainer);
if (authStatus === AuthenticationStatus.Unlocked) {
this.updateListItems(ciphers);
return;
}
this.buildLockedOverlay();
this.buildLockedInlineMenu();
}
/**
* Builds the locked overlay, which is displayed when the user is not authenticated.
* Facilitates the ability to unlock the extension from the overlay.
* Builds the locked inline menu, which is displayed when the user is not authenticated.
* Facilitates the ability to unlock the extension from the inline menu.
*/
private buildLockedOverlay() {
const lockedOverlay = globalThis.document.createElement("div");
lockedOverlay.id = "locked-overlay-description";
lockedOverlay.classList.add("locked-overlay", "overlay-list-message");
lockedOverlay.textContent = this.getTranslation("unlockYourAccount");
private buildLockedInlineMenu() {
const lockedInlineMenu = globalThis.document.createElement("div");
lockedInlineMenu.id = "locked-inline-menu-description";
lockedInlineMenu.classList.add("locked-inline-menu", "inline-menu-list-message");
lockedInlineMenu.textContent = this.getTranslation("unlockYourAccount");
const unlockButtonElement = globalThis.document.createElement("button");
unlockButtonElement.id = "unlock-button";
unlockButtonElement.tabIndex = -1;
unlockButtonElement.classList.add("unlock-button", "overlay-list-button");
unlockButtonElement.classList.add("unlock-button", "inline-menu-list-button");
unlockButtonElement.textContent = this.getTranslation("unlockAccount");
unlockButtonElement.setAttribute(
"aria-label",
@@ -100,11 +101,11 @@ export class AutofillInlineMenuList extends AutofillInlineMenuPageElement {
unlockButtonElement.prepend(buildSvgDomElement(lockIcon));
unlockButtonElement.addEventListener(EVENTS.CLICK, this.handleUnlockButtonClick);
const overlayListButtonContainer = globalThis.document.createElement("div");
overlayListButtonContainer.classList.add("overlay-list-button-container");
overlayListButtonContainer.appendChild(unlockButtonElement);
const inlineMenuListButtonContainer = globalThis.document.createElement("div");
inlineMenuListButtonContainer.classList.add("inline-menu-list-button-container");
inlineMenuListButtonContainer.appendChild(unlockButtonElement);
this.overlayListContainer.append(lockedOverlay, overlayListButtonContainer);
this.inlineMenuListContainer.append(lockedInlineMenu, inlineMenuListButtonContainer);
}
/**
@@ -117,45 +118,45 @@ export class AutofillInlineMenuList extends AutofillInlineMenuPageElement {
/**
* Updates the list items with the passed ciphers.
* If no ciphers are passed, the no results overlay is built.
* If no ciphers are passed, the no results inline menu is built.
*
* @param ciphers - The ciphers to display in the overlay list.
* @param ciphers - The ciphers to display in the inline menu list.
*/
private updateListItems(ciphers: OverlayCipherData[]) {
this.ciphers = ciphers;
this.currentCipherIndex = 0;
if (this.overlayListContainer) {
this.overlayListContainer.innerHTML = "";
if (this.inlineMenuListContainer) {
this.inlineMenuListContainer.innerHTML = "";
}
if (!ciphers?.length) {
this.buildNoResultsOverlayList();
this.buildNoResultsInlineMenuList();
return;
}
this.ciphersList = globalThis.document.createElement("ul");
this.ciphersList.classList.add("overlay-actions-list");
this.ciphersList.classList.add("inline-menu-list-actions");
this.ciphersList.setAttribute("role", "list");
globalThis.addEventListener(EVENTS.SCROLL, this.handleCiphersListScrollEvent);
this.loadPageOfCiphers();
this.overlayListContainer.appendChild(this.ciphersList);
this.inlineMenuListContainer.appendChild(this.ciphersList);
}
/**
* Overlay view that is presented when no ciphers are found for a given page.
* Facilitates the ability to add a new vault item from the overlay.
* Inline menu view that is presented when no ciphers are found for a given page.
* Facilitates the ability to add a new vault item from the inline menu.
*/
private buildNoResultsOverlayList() {
private buildNoResultsInlineMenuList() {
const noItemsMessage = globalThis.document.createElement("div");
noItemsMessage.classList.add("no-items", "overlay-list-message");
noItemsMessage.classList.add("no-items", "inline-menu-list-message");
noItemsMessage.textContent = this.getTranslation("noItemsToShow");
const newItemButton = globalThis.document.createElement("button");
newItemButton.tabIndex = -1;
newItemButton.id = "new-item-button";
newItemButton.classList.add("add-new-item-button", "overlay-list-button");
newItemButton.classList.add("add-new-item-button", "inline-menu-list-button");
newItemButton.textContent = this.getTranslation("newItem");
newItemButton.setAttribute(
"aria-label",
@@ -164,11 +165,11 @@ export class AutofillInlineMenuList extends AutofillInlineMenuPageElement {
newItemButton.prepend(buildSvgDomElement(plusIcon));
newItemButton.addEventListener(EVENTS.CLICK, this.handeNewItemButtonClick);
const overlayListButtonContainer = globalThis.document.createElement("div");
overlayListButtonContainer.classList.add("overlay-list-button-container");
overlayListButtonContainer.appendChild(newItemButton);
const inlineMenuListButtonContainer = globalThis.document.createElement("div");
inlineMenuListButtonContainer.classList.add("inline-menu-list-button-container");
inlineMenuListButtonContainer.appendChild(newItemButton);
this.overlayListContainer.append(noItemsMessage, overlayListButtonContainer);
this.inlineMenuListContainer.append(noItemsMessage, inlineMenuListButtonContainer);
}
/**
@@ -180,7 +181,7 @@ export class AutofillInlineMenuList extends AutofillInlineMenuPageElement {
};
/**
* Loads a page of ciphers into the overlay list container.
* Loads a page of ciphers into the inline menu list container.
*/
private loadPageOfCiphers() {
const lastIndex = Math.min(
@@ -188,7 +189,7 @@ export class AutofillInlineMenuList extends AutofillInlineMenuPageElement {
this.ciphers.length,
);
for (let cipherIndex = this.currentCipherIndex; cipherIndex < lastIndex; cipherIndex++) {
this.ciphersList.appendChild(this.buildOverlayActionsListItem(this.ciphers[cipherIndex]));
this.ciphersList.appendChild(this.buildInlineMenuListActionsItem(this.ciphers[cipherIndex]));
this.currentCipherIndex++;
}
@@ -230,7 +231,7 @@ export class AutofillInlineMenuList extends AutofillInlineMenuPageElement {
*
* @param cipher - The cipher to build the list item for.
*/
private buildOverlayActionsListItem(cipher: OverlayCipherData) {
private buildInlineMenuListActionsItem(cipher: OverlayCipherData) {
const fillCipherElement = this.buildFillCipherElement(cipher);
const viewCipherElement = this.buildViewCipherElement(cipher);
@@ -238,12 +239,12 @@ export class AutofillInlineMenuList extends AutofillInlineMenuPageElement {
cipherContainerElement.classList.add("cipher-container");
cipherContainerElement.append(fillCipherElement, viewCipherElement);
const overlayActionsListItem = globalThis.document.createElement("li");
overlayActionsListItem.setAttribute("role", "listitem");
overlayActionsListItem.classList.add("overlay-actions-list-item");
overlayActionsListItem.appendChild(cipherContainerElement);
const inlineMenuListActionsItem = globalThis.document.createElement("li");
inlineMenuListActionsItem.setAttribute("role", "listitem");
inlineMenuListActionsItem.classList.add("inline-menu-list-actions-item");
inlineMenuListActionsItem.appendChild(cipherContainerElement);
return overlayActionsListItem;
return inlineMenuListActionsItem;
}
/**
@@ -306,7 +307,7 @@ export class AutofillInlineMenuList extends AutofillInlineMenuPageElement {
event.preventDefault();
const currentListItem = event.target.closest(".overlay-actions-list-item") as HTMLElement;
const currentListItem = event.target.closest(".inline-menu-list-actions-item") as HTMLElement;
if (event.code === "ArrowDown") {
this.focusNextListItem(currentListItem);
return;
@@ -369,7 +370,7 @@ export class AutofillInlineMenuList extends AutofillInlineMenuPageElement {
event.preventDefault();
const currentListItem = event.target.closest(".overlay-actions-list-item") as HTMLElement;
const currentListItem = event.target.closest(".inline-menu-list-actions-item") as HTMLElement;
const cipherContainer = currentListItem.querySelector(".cipher-container") as HTMLElement;
cipherContainer?.classList.remove("remove-outline");
if (event.code === "ArrowDown") {
@@ -484,7 +485,7 @@ export class AutofillInlineMenuList extends AutofillInlineMenuPageElement {
}
/**
* Validates whether the overlay list iframe is currently focused.
* Validates whether the inline menu list iframe is currently focused.
* If not focused, will check if the button element is focused.
*/
private checkInlineMenuListFocused() {
@@ -496,15 +497,15 @@ export class AutofillInlineMenuList extends AutofillInlineMenuPageElement {
}
/**
* Focuses the overlay list iframe. The element that receives focus is
* Focuses the inline menu list iframe. The element that receives focus is
* determined by the presence of the unlock button, new item button, or
* the first cipher button.
*/
private focusInlineMenuList() {
this.overlayListContainer.setAttribute("role", "dialog");
this.overlayListContainer.setAttribute("aria-modal", "true");
this.inlineMenuListContainer.setAttribute("role", "dialog");
this.inlineMenuListContainer.setAttribute("aria-modal", "true");
const unlockButtonElement = this.overlayListContainer.querySelector(
const unlockButtonElement = this.inlineMenuListContainer.querySelector(
"#unlock-button",
) as HTMLElement;
if (unlockButtonElement) {
@@ -512,7 +513,7 @@ export class AutofillInlineMenuList extends AutofillInlineMenuPageElement {
return;
}
const newItemButtonElement = this.overlayListContainer.querySelector(
const newItemButtonElement = this.inlineMenuListContainer.querySelector(
"#new-item-button",
) as HTMLElement;
if (newItemButtonElement) {
@@ -520,31 +521,31 @@ export class AutofillInlineMenuList extends AutofillInlineMenuPageElement {
return;
}
const firstCipherElement = this.overlayListContainer.querySelector(
const firstCipherElement = this.inlineMenuListContainer.querySelector(
".fill-cipher-button",
) as HTMLElement;
firstCipherElement?.focus();
}
/**
* Sets up the global listeners for the overlay list iframe.
* Sets up the global listeners for the inline menu list iframe.
*/
private setupOverlayListGlobalListeners() {
this.setupGlobalListeners(this.overlayListWindowMessageHandlers);
private setupInlineMenuListGlobalListeners() {
this.setupGlobalListeners(this.inlineMenuListWindowMessageHandlers);
this.resizeObserver = new ResizeObserver(this.handleResizeObserver);
}
/**
* Handles the resize observer event. Facilitates updating the height of the
* overlay list iframe when the height of the list changes.
* inline menu list iframe when the height of the list changes.
*
* @param entries - The resize observer entries.
*/
private handleResizeObserver = (entries: ResizeObserverEntry[]) => {
for (let entryIndex = 0; entryIndex < entries.length; entryIndex++) {
const entry = entries[entryIndex];
if (entry.target !== this.overlayListContainer) {
if (entry.target !== this.inlineMenuListContainer) {
continue;
}
@@ -568,7 +569,7 @@ export class AutofillInlineMenuList extends AutofillInlineMenuPageElement {
};
/**
* Focuses the next list item in the overlay list. If the current list item is the last
* Focuses the next list item in the inline menu list. If the current list item is the last
* item in the list, the first item is focused.
*
* @param currentListItem - The current list item.
@@ -587,7 +588,7 @@ export class AutofillInlineMenuList extends AutofillInlineMenuPageElement {
}
/**
* Focuses the previous list item in the overlay list. If the current list item is the first
* Focuses the previous list item in the inline menu list. If the current list item is the first
* item in the list, the last item is focused.
*
* @param currentListItem - The current list item.

View File

@@ -22,7 +22,7 @@ body {
}
}
.overlay-list-message {
.inline-menu-list-message {
font-family: $font-family-sans-serif;
font-weight: 400;
font-size: 1.4rem;
@@ -39,7 +39,7 @@ body {
}
}
.overlay-list-button-container {
.inline-menu-list-button-container {
width: 100%;
padding: 0.2rem;
background: transparent;
@@ -58,7 +58,7 @@ body {
}
}
.overlay-list-button {
.inline-menu-list-button {
display: flex;
align-content: center;
justify-content: flex-start;
@@ -116,12 +116,12 @@ body {
}
}
.overlay-actions-list {
.inline-menu-list-actions {
padding: 0;
margin: 0;
}
.overlay-actions-list-item {
.inline-menu-list-actions-item {
transition: background-color 0.2s ease-in-out;
list-style: none;
padding: 0.2rem;

View File

@@ -195,7 +195,7 @@ function createAutofillOverlayCipherDataMock(index: number, customFields = {}):
};
}
function createInitAutofillOverlayListMessageMock(
function createInitAutofillInlineMenuListMessageMock(
customFields = {},
): InitAutofillInlineMenuListMessage {
return {
@@ -298,7 +298,7 @@ export {
createGenerateFillScriptOptionsMock,
createAutofillScriptMock,
createInitAutofillInlineMenuButtonMessageMock,
createInitAutofillOverlayListMessageMock,
createInitAutofillInlineMenuListMessageMock,
createFocusedFieldDataMock,
createPortSpyMock,
createMutationRecordMock,