Bug 1850591 - Convert Firefox View's domain-specific navigation component to a global component r=desktop-theme-reviewers,fxview-reviewers,reusable-components-reviewers,sclements,jules,hjones,fluent-reviewers,flod
Differential Revision: https://phabricator.services.mozilla.com/D193593
|
|
@ -10,12 +10,21 @@ const {
|
|||
"resource://testing-common/FirefoxViewTestUtils.sys.mjs"
|
||||
);
|
||||
|
||||
/**
|
||||
* Bug 1856572 - This test is to ensure that fluent strings in Firefox View
|
||||
* can be updated after opening Customize Mode
|
||||
* **/
|
||||
add_task(async function test_data_l10n_customize_mode() {
|
||||
FirefoxViewTestUtilsInit(this);
|
||||
await withFirefoxView({ win: window }, async function (browser) {
|
||||
/**
|
||||
* Bug 1856572, Bug 1857622: Without requesting two animation frames
|
||||
* the "missing Fluent strings" issue will not reproduce.
|
||||
*
|
||||
* Given the precondition that we open Firefox View, then open Customize Mode, then
|
||||
* navigate back to Firefox View in a quick succession, as long as Fluent
|
||||
* controlled strings can be updated by changing their Fluent IDs then this
|
||||
* test is valid.
|
||||
*/
|
||||
await new Promise(r =>
|
||||
requestAnimationFrame(() => requestAnimationFrame(r))
|
||||
|
|
@ -23,39 +32,47 @@ add_task(async function test_data_l10n_customize_mode() {
|
|||
await startCustomizing();
|
||||
await endCustomizing();
|
||||
await openFirefoxViewTab(window);
|
||||
|
||||
const { document } = browser.contentWindow;
|
||||
let header = document.querySelector("h1");
|
||||
document.l10n.setAttributes(header, "firefoxview-overview-header");
|
||||
let secondPageNavButton = document.querySelectorAll(
|
||||
"moz-page-nav-button"
|
||||
)[0];
|
||||
document.l10n.setAttributes(
|
||||
secondPageNavButton,
|
||||
"firefoxview-overview-header"
|
||||
);
|
||||
let previousText = await document.l10n.formatValue(
|
||||
"firefoxview-page-title"
|
||||
"firefoxview-opentabs-nav"
|
||||
);
|
||||
let translatedText = await window.content.document.l10n.formatValue(
|
||||
"firefoxview-overview-header"
|
||||
);
|
||||
/**
|
||||
* This should be replaced with
|
||||
* BrowserTestUtils.waitForMutationCondition(header, {characterData: true}, ...)
|
||||
* BrowserTestUtils.waitForMutationCondition(secondPageNavButton, {characterData: true}, ...)
|
||||
* but apparently Fluent manipulation of textContent doesn't result
|
||||
* in a characterData mutation occurring.
|
||||
*/
|
||||
await BrowserTestUtils.waitForCondition(() => {
|
||||
return header.textContent != previousText;
|
||||
return (
|
||||
secondPageNavButton.textContent !== previousText &&
|
||||
secondPageNavButton.textContent === translatedText
|
||||
);
|
||||
}, "waiting for text content to change");
|
||||
|
||||
Assert.equal(
|
||||
header.getAttribute("data-l10n-id"),
|
||||
secondPageNavButton.getAttribute("data-l10n-id"),
|
||||
"firefoxview-overview-header",
|
||||
"data-l10n-id should be updated"
|
||||
);
|
||||
Assert.notEqual(
|
||||
previousText,
|
||||
header.textContent,
|
||||
"The header's text content should be updated"
|
||||
);
|
||||
let translatedText = await window.content.document.l10n.formatValue(
|
||||
"firefoxview-overview-header"
|
||||
secondPageNavButton.textContent,
|
||||
"The second page-nav button text content should be updated"
|
||||
);
|
||||
|
||||
Assert.equal(
|
||||
translatedText,
|
||||
header.textContent,
|
||||
secondPageNavButton.textContent,
|
||||
"The changed text should be the translated value of 'firefoxview-overview-header"
|
||||
);
|
||||
});
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 655 B After Width: | Height: | Size: 655 B |
|
Before Width: | Height: | Size: 558 B After Width: | Height: | Size: 558 B |
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 658 B After Width: | Height: | Size: 658 B |
|
Before Width: | Height: | Size: 743 B After Width: | Height: | Size: 743 B |
|
|
@ -69,6 +69,10 @@ body {
|
|||
grid-template-columns: var(--fxview-sidebar-width) 1fr;
|
||||
background-color: var(--fxview-background-color);
|
||||
color: var(--fxview-text-primary-color);
|
||||
|
||||
@media (max-width: 52rem) {
|
||||
display: flex;
|
||||
}
|
||||
}
|
||||
|
||||
.main-container {
|
||||
|
|
@ -88,26 +92,6 @@ body {
|
|||
margin: 0;
|
||||
}
|
||||
|
||||
fxview-category-button:focus-visible {
|
||||
outline-offset: var(--in-content-focus-outline-inset);
|
||||
}
|
||||
|
||||
fxview-category-button[name="recentbrowsing"]::part(icon) {
|
||||
background-image: url("chrome://browser/content/firefoxview/category-recentbrowsing.svg");
|
||||
}
|
||||
fxview-category-button[name="opentabs"]::part(icon) {
|
||||
background-image: url("chrome://browser/content/firefoxview/category-opentabs.svg");
|
||||
}
|
||||
fxview-category-button[name="recentlyclosed"]::part(icon) {
|
||||
background-image: url("chrome://browser/content/firefoxview/category-recentlyclosed.svg");
|
||||
}
|
||||
fxview-category-button[name="syncedtabs"]::part(icon) {
|
||||
background-image: url("chrome://browser/content/firefoxview/category-syncedtabs.svg");
|
||||
}
|
||||
fxview-category-button[name="history"]::part(icon) {
|
||||
background-image: url("chrome://browser/content/firefoxview/category-history.svg");
|
||||
}
|
||||
|
||||
fxview-tab-list.with-dismiss-button::part(secondary-button) {
|
||||
background-image: url("chrome://global/skin/icons/close.svg");
|
||||
}
|
||||
|
|
@ -174,14 +158,6 @@ panel-list {
|
|||
overflow-y: visible;
|
||||
}
|
||||
|
||||
fxview-category-navigation {
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
fxview-category-navigation h1 {
|
||||
margin-block: 0;
|
||||
}
|
||||
|
||||
fxview-empty-state:not([isSelectedTab]) button[slot="primary-action"] {
|
||||
margin-inline-start: 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -41,54 +41,51 @@
|
|||
></script>
|
||||
<script
|
||||
type="module"
|
||||
src="chrome://browser/content/firefoxview/fxview-category-navigation.mjs"
|
||||
src="chrome://browser/content/firefoxview/syncedtabs.mjs"
|
||||
></script>
|
||||
<script
|
||||
type="module"
|
||||
src="chrome://browser/content/firefoxview/syncedtabs.mjs"
|
||||
src="chrome://global/content/elements/moz-page-nav.mjs"
|
||||
></script>
|
||||
<script src="chrome://browser/content/contentTheme.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<fxview-category-navigation>
|
||||
<h1 slot="category-nav-header" data-l10n-id="firefoxview-page-title"></h1>
|
||||
<fxview-category-button
|
||||
class="category"
|
||||
slot="category-button"
|
||||
name="recentbrowsing"
|
||||
<moz-page-nav
|
||||
data-l10n-id="firefoxview-page-heading"
|
||||
data-l10n-attrs="heading"
|
||||
>
|
||||
<moz-page-nav-button
|
||||
view="recentbrowsing"
|
||||
data-l10n-id="firefoxview-overview-nav"
|
||||
iconSrc="chrome://browser/content/firefoxview/view-recentbrowsing.svg"
|
||||
>
|
||||
</fxview-category-button>
|
||||
<fxview-category-button
|
||||
class="category"
|
||||
slot="category-button"
|
||||
name="opentabs"
|
||||
</moz-page-nav-button>
|
||||
<moz-page-nav-button
|
||||
view="opentabs"
|
||||
data-l10n-id="firefoxview-opentabs-nav"
|
||||
iconSrc="chrome://browser/content/firefoxview/view-opentabs.svg"
|
||||
>
|
||||
</fxview-category-button>
|
||||
<fxview-category-button
|
||||
class="category"
|
||||
slot="category-button"
|
||||
name="recentlyclosed"
|
||||
</moz-page-nav-button>
|
||||
<moz-page-nav-button
|
||||
view="recentlyclosed"
|
||||
data-l10n-id="firefoxview-recently-closed-nav"
|
||||
iconSrc="chrome://browser/content/firefoxview/view-recentlyclosed.svg"
|
||||
>
|
||||
</fxview-category-button>
|
||||
<fxview-category-button
|
||||
class="category"
|
||||
slot="category-button"
|
||||
name="syncedtabs"
|
||||
</moz-page-nav-button>
|
||||
<moz-page-nav-button
|
||||
view="syncedtabs"
|
||||
data-l10n-id="firefoxview-synced-tabs-nav"
|
||||
iconSrc="chrome://browser/content/firefoxview/view-syncedtabs.svg"
|
||||
>
|
||||
</fxview-category-button>
|
||||
<fxview-category-button
|
||||
class="category"
|
||||
slot="category-button"
|
||||
name="history"
|
||||
</moz-page-nav-button>
|
||||
<moz-page-nav-button
|
||||
view="history"
|
||||
data-l10n-id="firefoxview-history-nav"
|
||||
iconSrc="chrome://browser/content/firefoxview/view-history.svg"
|
||||
>
|
||||
</fxview-category-button>
|
||||
</fxview-category-navigation>
|
||||
</moz-page-nav-button>
|
||||
</moz-page-nav>
|
||||
<main id="pages" role="application" data-l10n-id="firefoxview-page-label">
|
||||
<div class="main-container">
|
||||
<named-deck>
|
||||
|
|
|
|||
|
|
@ -3,35 +3,35 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
let pageList = [];
|
||||
let categoryPagesDeck = null;
|
||||
let categoryNavigation = null;
|
||||
let viewsDeck = null;
|
||||
let pageNav = null;
|
||||
let activeComponent = null;
|
||||
let searchKeyboardShortcut = null;
|
||||
|
||||
const { topChromeWindow } = window.browsingContext;
|
||||
|
||||
function onHashChange() {
|
||||
let page = document.location?.hash.substring(1);
|
||||
if (!page || !pageList.includes(page)) {
|
||||
page = "recentbrowsing";
|
||||
let view = document.location?.hash.substring(1);
|
||||
if (!view || !pageList.includes(view)) {
|
||||
view = "recentbrowsing";
|
||||
}
|
||||
changePage(page);
|
||||
changeView(view);
|
||||
}
|
||||
|
||||
function changePage(page) {
|
||||
categoryPagesDeck.selectedViewName = page;
|
||||
categoryNavigation.currentCategory = page;
|
||||
if (categoryNavigation.categoryButtons.includes(document.activeElement)) {
|
||||
let currentCategoryButton = categoryNavigation.categoryButtons.find(
|
||||
categoryButton => categoryButton.name === page
|
||||
function changeView(view) {
|
||||
viewsDeck.selectedViewName = view;
|
||||
pageNav.currentPage = view;
|
||||
if (pageNav.pageNavButtons.includes(document.activeElement)) {
|
||||
let currentPageButton = pageNav.pageNavButtons.find(
|
||||
pageButton => pageButton.view === view
|
||||
);
|
||||
(currentCategoryButton || categoryNavigation.categoryButtons[0]).focus();
|
||||
(currentPageButton || pageNav.pageNavButtons[0]).focus();
|
||||
}
|
||||
}
|
||||
|
||||
function onPagesDeckViewChange() {
|
||||
for (const child of categoryPagesDeck.children) {
|
||||
if (child.getAttribute("name") == categoryPagesDeck.selectedViewName) {
|
||||
function onViewsDeckViewChange() {
|
||||
for (const child of viewsDeck.children) {
|
||||
if (child.getAttribute("name") == viewsDeck.selectedViewName) {
|
||||
child.enter();
|
||||
activeComponent = child;
|
||||
} else {
|
||||
|
|
@ -41,11 +41,11 @@ function onPagesDeckViewChange() {
|
|||
}
|
||||
|
||||
function recordNavigationTelemetry(source, eventTarget) {
|
||||
let page = "recentbrowsing";
|
||||
let view = "recentbrowsing";
|
||||
if (source === "category-navigation") {
|
||||
page = eventTarget.parentNode.currentCategory;
|
||||
view = eventTarget.parentNode.currentView;
|
||||
} else if (source === "view-all") {
|
||||
page = eventTarget.shortPageName;
|
||||
view = eventTarget.shortPageName;
|
||||
}
|
||||
// Record telemetry
|
||||
Services.telemetry.recordEvent(
|
||||
|
|
@ -54,7 +54,7 @@ function recordNavigationTelemetry(source, eventTarget) {
|
|||
"navigation",
|
||||
null,
|
||||
{
|
||||
page,
|
||||
page: view,
|
||||
source,
|
||||
}
|
||||
);
|
||||
|
|
@ -73,7 +73,7 @@ async function updateSearchTextboxSize() {
|
|||
const placeholder = msg.attributes[0].value;
|
||||
maxLength = Math.max(maxLength, placeholder.length);
|
||||
}
|
||||
for (const child of categoryPagesDeck.children) {
|
||||
for (const child of viewsDeck.children) {
|
||||
child.searchTextboxSize = maxLength;
|
||||
}
|
||||
}
|
||||
|
|
@ -89,15 +89,15 @@ async function updateSearchKeyboardShortcut() {
|
|||
window.addEventListener("DOMContentLoaded", async () => {
|
||||
recordEnteredTelemetry();
|
||||
|
||||
categoryNavigation = document.querySelector("fxview-category-navigation");
|
||||
categoryPagesDeck = document.querySelector("named-deck");
|
||||
pageNav = document.querySelector("moz-page-nav");
|
||||
viewsDeck = document.querySelector("named-deck");
|
||||
|
||||
for (const item of categoryNavigation.categoryButtons) {
|
||||
pageList.push(item.getAttribute("name"));
|
||||
for (const item of pageNav.pageNavButtons) {
|
||||
pageList.push(item.getAttribute("view"));
|
||||
}
|
||||
window.addEventListener("hashchange", onHashChange);
|
||||
window.addEventListener("change-category", function (event) {
|
||||
location.hash = event.target.getAttribute("name");
|
||||
window.addEventListener("change-view", function (event) {
|
||||
location.hash = event.target.getAttribute("view");
|
||||
window.scrollTo(0, 0);
|
||||
recordNavigationTelemetry("category-navigation", event.target);
|
||||
});
|
||||
|
|
@ -105,11 +105,11 @@ window.addEventListener("DOMContentLoaded", async () => {
|
|||
recordNavigationTelemetry("view-all", event.originalTarget);
|
||||
});
|
||||
|
||||
categoryPagesDeck.addEventListener("view-changed", onPagesDeckViewChange);
|
||||
viewsDeck.addEventListener("view-changed", onViewsDeckViewChange);
|
||||
|
||||
// set the initial state
|
||||
onHashChange();
|
||||
onPagesDeckViewChange();
|
||||
onViewsDeckViewChange();
|
||||
await updateSearchTextboxSize();
|
||||
await updateSearchKeyboardShortcut();
|
||||
|
||||
|
|
|
|||
|
|
@ -1,60 +0,0 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
:host {
|
||||
--fxviewcategorynav-button-padding: 8px;
|
||||
margin-inline-start: 42px;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
nav {
|
||||
display: grid;
|
||||
grid-template-rows: min-content 1fr auto;
|
||||
gap: 25px;
|
||||
margin-block-start: var(--fxview-margin-top);
|
||||
}
|
||||
|
||||
.category-nav-header {
|
||||
/* Align the header text/icon with the category button icons */
|
||||
margin-inline-start: var(--fxviewcategorynav-button-padding);
|
||||
}
|
||||
|
||||
.category-nav-buttons,
|
||||
::slotted(.category-nav-footer) {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr;
|
||||
grid-auto-rows: min-content;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
@media (prefers-contrast) {
|
||||
.category-nav-buttons {
|
||||
gap: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion) {
|
||||
/* (See Bug 1610081) Setting border-inline-end to add clear differentiation between side navigation and main content area */
|
||||
:host {
|
||||
border-inline-end: 1px solid var(--in-content-border-color);
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 52rem) {
|
||||
:host {
|
||||
grid-template-rows: 1fr auto;
|
||||
}
|
||||
|
||||
.category-nav-header {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.category-nav-buttons,
|
||||
::slotted(.category-nav-footer) {
|
||||
justify-content: center;
|
||||
grid-template-columns: min-content;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,150 +0,0 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
import { html } from "chrome://global/content/vendor/lit.all.mjs";
|
||||
import { MozLitElement } from "chrome://global/content/lit-utils.mjs";
|
||||
|
||||
export default class FxviewCategoryNavigation extends MozLitElement {
|
||||
static properties = {
|
||||
currentCategory: { type: String },
|
||||
};
|
||||
|
||||
static queries = {
|
||||
categoryButtonsSlot: "slot[name=category-button]",
|
||||
};
|
||||
|
||||
get categoryButtons() {
|
||||
return this.categoryButtonsSlot
|
||||
.assignedNodes()
|
||||
.filter(node => !node.hidden);
|
||||
}
|
||||
|
||||
onChangeCategory(e) {
|
||||
this.currentCategory = e.target.name;
|
||||
}
|
||||
|
||||
handleFocus(e) {
|
||||
if (e.key == "ArrowDown" || e.key == "ArrowRight") {
|
||||
e.preventDefault();
|
||||
this.focusNextCategory();
|
||||
} else if (e.key == "ArrowUp" || e.key == "ArrowLeft") {
|
||||
e.preventDefault();
|
||||
this.focusPreviousCategory();
|
||||
}
|
||||
}
|
||||
|
||||
focusPreviousCategory() {
|
||||
let categoryButtons = this.categoryButtons;
|
||||
let currentIndex = categoryButtons.findIndex(b => b.selected);
|
||||
let prev = categoryButtons[currentIndex - 1];
|
||||
if (prev) {
|
||||
prev.activate();
|
||||
prev.focus();
|
||||
}
|
||||
}
|
||||
|
||||
focusNextCategory() {
|
||||
let categoryButtons = this.categoryButtons;
|
||||
let currentIndex = categoryButtons.findIndex(b => b.selected);
|
||||
let next = categoryButtons[currentIndex + 1];
|
||||
if (next) {
|
||||
next.activate();
|
||||
next.focus();
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
return html`
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="chrome://browser/content/firefoxview/fxview-category-navigation.css"
|
||||
/>
|
||||
<nav>
|
||||
<div class="category-nav-header">
|
||||
<slot name="category-nav-header"></slot>
|
||||
</div>
|
||||
<div
|
||||
class="category-nav-buttons"
|
||||
role="tablist"
|
||||
aria-orientation="vertical"
|
||||
>
|
||||
<slot
|
||||
name="category-button"
|
||||
@change-category=${this.onChangeCategory}
|
||||
@keydown=${this.handleFocus}
|
||||
></slot>
|
||||
</div>
|
||||
<div class="category-nav-footer">
|
||||
<slot name="category-nav-footer"></slot>
|
||||
</div>
|
||||
</nav>
|
||||
`;
|
||||
}
|
||||
|
||||
updated() {
|
||||
let categorySelected = false;
|
||||
let assignedCategories = this.categoryButtons;
|
||||
for (let button of assignedCategories) {
|
||||
button.selected = button.name == this.currentCategory;
|
||||
categorySelected = categorySelected || button.selected;
|
||||
}
|
||||
if (!categorySelected && assignedCategories.length) {
|
||||
// Current category has no matching category, reset to the first category.
|
||||
assignedCategories[0].activate();
|
||||
}
|
||||
}
|
||||
}
|
||||
customElements.define("fxview-category-navigation", FxviewCategoryNavigation);
|
||||
|
||||
export class FxviewCategoryButton extends MozLitElement {
|
||||
static properties = {
|
||||
selected: { type: Boolean },
|
||||
};
|
||||
|
||||
static queries = {
|
||||
buttonEl: "button",
|
||||
};
|
||||
|
||||
connectedCallback() {
|
||||
super.connectedCallback();
|
||||
this.setAttribute("role", "tab");
|
||||
}
|
||||
|
||||
get name() {
|
||||
return this.getAttribute("name");
|
||||
}
|
||||
|
||||
activate() {
|
||||
this.dispatchEvent(
|
||||
new CustomEvent("change-category", {
|
||||
bubbles: true,
|
||||
composed: true,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
return html`
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="chrome://browser/content/firefoxview/fxview-category-button.css"
|
||||
/>
|
||||
<button
|
||||
aria-hidden="true"
|
||||
tabindex="-1"
|
||||
?selected=${this.selected}
|
||||
@click=${this.activate}
|
||||
>
|
||||
<span class="category-icon" part="icon"></span>
|
||||
<slot></slot>
|
||||
</button>
|
||||
`;
|
||||
}
|
||||
|
||||
updated() {
|
||||
this.setAttribute("aria-selected", this.selected);
|
||||
this.setAttribute("tabindex", this.selected ? 0 : -1);
|
||||
}
|
||||
}
|
||||
customElements.define("fxview-category-button", FxviewCategoryButton);
|
||||
|
|
@ -15,9 +15,6 @@ browser.jar:
|
|||
content/browser/firefoxview/view-syncedtabs.css
|
||||
content/browser/firefoxview/recentbrowsing.mjs
|
||||
content/browser/firefoxview/firefoxview.css
|
||||
content/browser/firefoxview/fxview-category-button.css
|
||||
content/browser/firefoxview/fxview-category-navigation.css
|
||||
content/browser/firefoxview/fxview-category-navigation.mjs
|
||||
content/browser/firefoxview/fxview-empty-state.css
|
||||
content/browser/firefoxview/fxview-empty-state.mjs
|
||||
content/browser/firefoxview/helpers.mjs
|
||||
|
|
@ -29,12 +26,12 @@ browser.jar:
|
|||
content/browser/firefoxview/recentlyclosed.mjs
|
||||
content/browser/firefoxview/viewpage.mjs
|
||||
content/browser/firefoxview/history-empty.svg (content/history-empty.svg)
|
||||
content/browser/firefoxview/category-history.svg (content/category-history.svg)
|
||||
content/browser/firefoxview/category-opentabs.svg (content/category-opentabs.svg)
|
||||
content/browser/firefoxview/category-recentbrowsing.svg (content/category-recentbrowsing.svg)
|
||||
content/browser/firefoxview/category-recentlyclosed.svg (content/category-recentlyclosed.svg)
|
||||
content/browser/firefoxview/category-syncedtabs.svg (content/category-syncedtabs.svg)
|
||||
content/browser/firefoxview/recentlyclosed-empty.svg (content/recentlyclosed-empty.svg)
|
||||
content/browser/firefoxview/synced-tabs-error.svg (content/synced-tabs-error.svg)
|
||||
content/browser/callout-tab-pickup.svg (content/callout-tab-pickup.svg)
|
||||
content/browser/callout-tab-pickup-dark.svg (content/callout-tab-pickup-dark.svg)
|
||||
content/browser/firefoxview/view-history.svg (content/view-history.svg)
|
||||
content/browser/firefoxview/view-opentabs.svg (content/view-opentabs.svg)
|
||||
content/browser/firefoxview/view-recentbrowsing.svg (content/view-recentbrowsing.svg)
|
||||
content/browser/firefoxview/view-recentlyclosed.svg (content/view-recentlyclosed.svg)
|
||||
content/browser/firefoxview/view-syncedtabs.svg (content/view-syncedtabs.svg)
|
||||
|
|
|
|||
|
|
@ -27,37 +27,37 @@ skip-if = ["true"] # Bug 1869605 and # Bug 1870296
|
|||
|
||||
["browser_firefoxview.js"]
|
||||
|
||||
["browser_firefoxview_tab.js"]
|
||||
|
||||
["browser_notification_dot.js"]
|
||||
skip-if = ["true"] # Bug 1851453
|
||||
|
||||
["browser_opentabs_changes.js"]
|
||||
|
||||
["browser_reload_firefoxview.js"]
|
||||
|
||||
["browser_tab_close_last_tab.js"]
|
||||
|
||||
["browser_tab_on_close_warning.js"]
|
||||
|
||||
["browser_firefoxview_paused.js"]
|
||||
|
||||
["browser_firefoxview_general_telemetry.js"]
|
||||
fail-if = ["a11y_checks"] # Bug 1850591 clicked moz-page-nav-button button is not focusable
|
||||
|
||||
["browser_firefoxview_navigation.js"]
|
||||
fail-if = ["a11y_checks"] # Bug 1850591 clicked moz-page-nav-button button is not focusable
|
||||
|
||||
["browser_firefoxview_paused.js"]
|
||||
fail-if = ["a11y_checks"] # Bug 1850591 clicked moz-page-nav-button button is not focusable
|
||||
|
||||
["browser_firefoxview_search_telemetry.js"]
|
||||
fail-if = ["a11y_checks"] # Bug 1850591 clicked moz-page-nav-button button is not focusable
|
||||
|
||||
["browser_firefoxview_tab.js"]
|
||||
|
||||
["browser_firefoxview_virtual_list.js"]
|
||||
fail-if = ["a11y_checks"] # Bug 1850591 clicked moz-page-nav-button button is not focusable
|
||||
|
||||
["browser_history_firefoxview.js"]
|
||||
skip-if = ["true"] # Bug 1877594
|
||||
|
||||
["browser_opentabs_firefoxview.js"]
|
||||
["browser_notification_dot.js"]
|
||||
skip-if = ["true"] # Bug 1851453
|
||||
|
||||
["browser_opentabs_cards.js"]
|
||||
fail-if = ["a11y_checks"] # Bugs 1858041, 1854625, and 1872174 clicked Show all link is not accessible because it is "hidden" when clicked
|
||||
|
||||
["browser_opentabs_changes.js"]
|
||||
|
||||
["browser_opentabs_firefoxview.js"]
|
||||
fail-if = ["a11y_checks"] # Bug 1850591 clicked moz-page-nav-button button is not focusable
|
||||
|
||||
["browser_opentabs_recency.js"]
|
||||
skip-if = [
|
||||
"os == 'win'",
|
||||
|
|
@ -65,9 +65,19 @@ skip-if = [
|
|||
] # macos times out, see bug 1857293, skipped for windows, see bug 1858460
|
||||
|
||||
["browser_opentabs_tab_indicators.js"]
|
||||
fail-if = ["a11y_checks"] # Bug 1850591 clicked moz-page-nav-button button is not focusable
|
||||
|
||||
["browser_recentlyclosed_firefoxview.js"]
|
||||
fail-if = ["a11y_checks"] # Bug 1850591 clicked moz-page-nav-button button is not focusable
|
||||
|
||||
["browser_reload_firefoxview.js"]
|
||||
|
||||
["browser_syncedtabs_errors_firefoxview.js"]
|
||||
fail-if = ["a11y_checks"] # Bug 1850591 clicked moz-page-nav-button button is not focusable
|
||||
|
||||
["browser_syncedtabs_firefoxview.js"]
|
||||
fail-if = ["a11y_checks"] # Bug 1850591 clicked moz-page-nav-button button is not focusable
|
||||
|
||||
["browser_tab_close_last_tab.js"]
|
||||
|
||||
["browser_tab_on_close_warning.js"]
|
||||
|
|
|
|||
|
|
@ -6,10 +6,7 @@ add_task(async function about_firefoxview_smoke_test() {
|
|||
const { document } = browser.contentWindow;
|
||||
|
||||
// sanity check the important regions exist on this page
|
||||
ok(
|
||||
document.querySelector("fxview-category-navigation"),
|
||||
"fxview-category-navigation element exists"
|
||||
);
|
||||
ok(document.querySelector("moz-page-nav"), "moz-page-nav element exists");
|
||||
ok(document.querySelector("named-deck"), "named-deck element exists");
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ add_task(async function firefox_view_entered_telemetry() {
|
|||
enteredTelemetry[4] = { page: "recentlyclosed" };
|
||||
enteredAndTabSelectedEvents = [tabSelectedTelemetry, enteredTelemetry];
|
||||
|
||||
navigateToCategory(document, "recentlyclosed");
|
||||
await navigateToViewAndWait(document, "recentlyclosed");
|
||||
await clearAllParentTelemetryEvents();
|
||||
await BrowserTestUtils.openNewForegroundTab(gBrowser, "about:robots");
|
||||
is(
|
||||
|
|
@ -107,9 +107,9 @@ add_task(async function test_change_page_telemetry() {
|
|||
],
|
||||
];
|
||||
await clearAllParentTelemetryEvents();
|
||||
navigateToCategory(document, "recentlyclosed");
|
||||
await navigateToViewAndWait(document, "recentlyclosed");
|
||||
await telemetryEvent(changePageEvent);
|
||||
navigateToCategory(document, "recentbrowsing");
|
||||
await navigateToViewAndWait(document, "recentbrowsing");
|
||||
|
||||
let openTabsComponent = document.querySelector(
|
||||
"view-opentabs[slot=opentabs]"
|
||||
|
|
@ -189,7 +189,7 @@ add_task(async function test_context_menu_new_window_telemetry() {
|
|||
);
|
||||
|
||||
// Test history context menu options
|
||||
await navigateToCategoryAndWait(document, "history");
|
||||
await navigateToViewAndWait(document, "history");
|
||||
let historyComponent = document.querySelector("view-history");
|
||||
await TestUtils.waitForCondition(() => historyComponent.fullyUpdated);
|
||||
await TestUtils.waitForCondition(
|
||||
|
|
@ -245,7 +245,7 @@ add_task(async function test_context_menu_private_window_telemetry() {
|
|||
);
|
||||
|
||||
// Test history context menu options
|
||||
await navigateToCategoryAndWait(document, "history");
|
||||
await navigateToViewAndWait(document, "history");
|
||||
let historyComponent = document.querySelector("view-history");
|
||||
await TestUtils.waitForCondition(() => historyComponent.fullyUpdated);
|
||||
await TestUtils.waitForCondition(
|
||||
|
|
@ -314,7 +314,7 @@ add_task(async function test_context_menu_delete_from_history_telemetry() {
|
|||
);
|
||||
|
||||
// Test history context menu options
|
||||
await navigateToCategoryAndWait(document, "history");
|
||||
await navigateToViewAndWait(document, "history");
|
||||
let historyComponent = document.querySelector("view-history");
|
||||
await TestUtils.waitForCondition(() => historyComponent.fullyUpdated);
|
||||
await TestUtils.waitForCondition(
|
||||
|
|
|
|||
|
|
@ -3,15 +3,15 @@
|
|||
|
||||
const URL_BASE = `${getFirefoxViewURL()}#`;
|
||||
|
||||
function assertCorrectPage(document, name, event) {
|
||||
function assertCorrectPage(document, view, event) {
|
||||
is(
|
||||
document.location.hash,
|
||||
`#${name}`,
|
||||
`Navigation button for ${name} navigates to ${URL_BASE + name} on ${event}.`
|
||||
`#${view}`,
|
||||
`Navigation button for ${view} navigates to ${URL_BASE + view} on ${event}.`
|
||||
);
|
||||
is(
|
||||
document.querySelector("named-deck").selectedViewName,
|
||||
name,
|
||||
view,
|
||||
"The correct deck child is selected"
|
||||
);
|
||||
}
|
||||
|
|
@ -22,21 +22,21 @@ add_task(async function test_side_component_navigation_by_click() {
|
|||
|
||||
const { document } = browser.contentWindow;
|
||||
let win = browser.ownerGlobal;
|
||||
const categoryButtons = document.querySelectorAll("fxview-category-button");
|
||||
const pageNavButtons = document.querySelectorAll("moz-page-nav-button");
|
||||
|
||||
for (let element of categoryButtons) {
|
||||
const name = element.name;
|
||||
for (let element of pageNavButtons) {
|
||||
const view = element.view;
|
||||
let buttonClicked = BrowserTestUtils.waitForEvent(
|
||||
element.buttonEl,
|
||||
"click",
|
||||
win
|
||||
);
|
||||
|
||||
info(`Clicking navigation button for ${name}`);
|
||||
info(`Clicking navigation button for ${view}`);
|
||||
EventUtils.synthesizeMouseAtCenter(element.buttonEl, {}, content);
|
||||
await buttonClicked;
|
||||
|
||||
assertCorrectPage(document, name, "click");
|
||||
assertCorrectPage(document, view, "click");
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
@ -47,49 +47,49 @@ add_task(async function test_side_component_navigation_by_keyboard() {
|
|||
|
||||
const { document } = browser.contentWindow;
|
||||
let win = browser.ownerGlobal;
|
||||
const categoryButtons = document.querySelectorAll("fxview-category-button");
|
||||
const firstButton = categoryButtons[0];
|
||||
const pageNavButtons = document.querySelectorAll("moz-page-nav-button");
|
||||
const firstButton = pageNavButtons[0].buttonEl;
|
||||
|
||||
firstButton.focus();
|
||||
is(
|
||||
document.activeElement,
|
||||
document.activeElement.shadowRoot.activeElement,
|
||||
firstButton,
|
||||
"The first category button has focus"
|
||||
"The first page nav button has focus"
|
||||
);
|
||||
|
||||
for (let element of Array.from(categoryButtons).slice(1)) {
|
||||
const name = element.name;
|
||||
for (let element of Array.from(pageNavButtons).slice(1)) {
|
||||
const view = element.view;
|
||||
let buttonFocused = BrowserTestUtils.waitForEvent(element, "focus", win);
|
||||
|
||||
info(`Focus is on ${document.activeElement.name}`);
|
||||
info(`Arrow down on navigation to ${name}`);
|
||||
info(`Focus is on ${document.activeElement.view}`);
|
||||
info(`Arrow down on navigation to ${view}`);
|
||||
EventUtils.synthesizeKey("KEY_ArrowDown", {}, win);
|
||||
await buttonFocused;
|
||||
|
||||
assertCorrectPage(document, name, "key press");
|
||||
assertCorrectPage(document, view, "key press");
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
add_task(async function test_direct_navigation_to_correct_category() {
|
||||
add_task(async function test_direct_navigation_to_correct_view() {
|
||||
await withFirefoxView({}, async browser => {
|
||||
const { document } = browser.contentWindow;
|
||||
const categoryButtons = document.querySelectorAll("fxview-category-button");
|
||||
const pageNavButtons = document.querySelectorAll("moz-page-nav-button");
|
||||
const namedDeck = document.querySelector("named-deck");
|
||||
|
||||
for (let element of categoryButtons) {
|
||||
const name = element.name;
|
||||
for (let element of pageNavButtons) {
|
||||
const view = element.view;
|
||||
|
||||
info(`Navigating to ${URL_BASE + name}`);
|
||||
document.location.assign(URL_BASE + name);
|
||||
info(`Navigating to ${URL_BASE + view}`);
|
||||
document.location.assign(URL_BASE + view);
|
||||
await BrowserTestUtils.waitForCondition(() => {
|
||||
return namedDeck.selectedViewName === name;
|
||||
return namedDeck.selectedViewName === view;
|
||||
}, "Wait for navigation to complete");
|
||||
|
||||
is(
|
||||
namedDeck.selectedViewName,
|
||||
name,
|
||||
`The correct deck child for category ${name} is selected`
|
||||
view,
|
||||
`The correct deck child for view ${view} is selected`
|
||||
);
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -322,7 +322,7 @@ add_task(async function test_opentabs() {
|
|||
const document = browser.contentDocument;
|
||||
const { openTabsView } = getTopLevelViewElements(document);
|
||||
|
||||
await navigateToCategoryAndWait(document, "opentabs");
|
||||
await navigateToViewAndWait(document, "opentabs");
|
||||
|
||||
const { openTabsList } = await getElements(document);
|
||||
ok(openTabsView, "Found the open tabs view");
|
||||
|
|
@ -387,7 +387,7 @@ add_task(async function test_recentlyclosed() {
|
|||
await withFirefoxView({}, async browser => {
|
||||
const document = browser.contentDocument;
|
||||
const { recentlyClosedView } = getTopLevelViewElements(document);
|
||||
await navigateToCategoryAndWait(document, "recentlyclosed");
|
||||
await navigateToViewAndWait(document, "recentlyclosed");
|
||||
|
||||
const { recentlyClosedList } = await getElements(document);
|
||||
ok(recentlyClosedView, "Found the recently-closed view");
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@ add_task(async function test_search_initiated_telemetry() {
|
|||
EventUtils.sendString("example.com", content);
|
||||
await telemetryEvent(searchEvent("recentbrowsing"));
|
||||
|
||||
await navigateToCategoryAndWait(document, "opentabs");
|
||||
await navigateToViewAndWait(document, "opentabs");
|
||||
await clearAllParentTelemetryEvents();
|
||||
is(document.location.hash, "#opentabs", "Searching within open tabs.");
|
||||
const openTabs = document.querySelector("named-deck > view-opentabs");
|
||||
|
|
@ -65,7 +65,7 @@ add_task(async function test_search_initiated_telemetry() {
|
|||
EventUtils.sendString("example.com", content);
|
||||
await telemetryEvent(searchEvent("opentabs"));
|
||||
|
||||
await navigateToCategoryAndWait(document, "recentlyclosed");
|
||||
await navigateToViewAndWait(document, "recentlyclosed");
|
||||
await clearAllParentTelemetryEvents();
|
||||
is(
|
||||
document.location.hash,
|
||||
|
|
@ -84,7 +84,7 @@ add_task(async function test_search_initiated_telemetry() {
|
|||
EventUtils.sendString("example.com", content);
|
||||
await telemetryEvent(searchEvent("recentlyclosed"));
|
||||
|
||||
await navigateToCategoryAndWait(document, "syncedtabs");
|
||||
await navigateToViewAndWait(document, "syncedtabs");
|
||||
await clearAllParentTelemetryEvents();
|
||||
is(document.location.hash, "#syncedtabs", "Searching within synced tabs.");
|
||||
const syncedTabs = document.querySelector("named-deck > view-syncedtabs");
|
||||
|
|
@ -93,7 +93,7 @@ add_task(async function test_search_initiated_telemetry() {
|
|||
EventUtils.sendString("example.com", content);
|
||||
await telemetryEvent(searchEvent("syncedtabs"));
|
||||
|
||||
await navigateToCategoryAndWait(document, "history");
|
||||
await navigateToViewAndWait(document, "history");
|
||||
await clearAllParentTelemetryEvents();
|
||||
is(document.location.hash, "#history", "Searching within history.");
|
||||
const history = document.querySelector("named-deck > view-history");
|
||||
|
|
@ -310,7 +310,7 @@ add_task(async function test_sort_history_search_telemetry() {
|
|||
|
||||
await withFirefoxView({}, async browser => {
|
||||
const { document } = browser.contentWindow;
|
||||
await navigateToCategoryAndWait(document, "history");
|
||||
await navigateToViewAndWait(document, "history");
|
||||
const historyComponent = document.querySelector("view-history");
|
||||
|
||||
const searchTextbox = await TestUtils.waitForCondition(
|
||||
|
|
@ -426,7 +426,7 @@ add_task(async function test_cumulative_searches_recently_closed_telemetry() {
|
|||
await withFirefoxView({}, async browser => {
|
||||
const { document } = browser.contentWindow;
|
||||
|
||||
await navigateToCategoryAndWait(document, "recentlyclosed");
|
||||
await navigateToViewAndWait(document, "recentlyclosed");
|
||||
is(
|
||||
document.location.hash,
|
||||
"#recentlyclosed",
|
||||
|
|
@ -471,7 +471,7 @@ add_task(async function test_cumulative_searches_open_tabs_telemetry() {
|
|||
await withFirefoxView({}, async browser => {
|
||||
const { document } = browser.contentWindow;
|
||||
|
||||
await navigateToCategoryAndWait(document, "opentabs");
|
||||
await navigateToViewAndWait(document, "opentabs");
|
||||
is(document.location.hash, "#opentabs", "Searching within open tabs.");
|
||||
const openTabs = document.querySelector("named-deck > view-opentabs");
|
||||
|
||||
|
|
@ -517,7 +517,7 @@ add_task(async function test_cumulative_searches_history_telemetry() {
|
|||
await withFirefoxView({}, async browser => {
|
||||
const { document } = browser.contentWindow;
|
||||
|
||||
await navigateToCategoryAndWait(document, "history");
|
||||
await navigateToViewAndWait(document, "history");
|
||||
is(document.location.hash, "#history", "Searching within history.");
|
||||
const history = document.querySelector("named-deck > view-history");
|
||||
const searchTextbox = await TestUtils.waitForCondition(() => {
|
||||
|
|
@ -579,7 +579,7 @@ add_task(async function test_cumulative_searches_syncedtabs_telemetry() {
|
|||
const { document } = browser.contentWindow;
|
||||
Services.obs.notifyObservers(null, UIState.ON_UPDATE);
|
||||
|
||||
await navigateToCategoryAndWait(document, "syncedtabs");
|
||||
await navigateToViewAndWait(document, "syncedtabs");
|
||||
is(document.location.hash, "#syncedtabs", "Searching within synced tabs.");
|
||||
let syncedTabs = document.querySelector(
|
||||
"view-syncedtabs:not([slot=syncedtabs])"
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ add_task(async function test_max_render_count_on_win_resize() {
|
|||
"Firefox View is loaded to the Recent Browsing page."
|
||||
);
|
||||
|
||||
await navigateToCategoryAndWait(document, "history");
|
||||
await navigateToViewAndWait(document, "history");
|
||||
|
||||
let historyComponent = document.querySelector("view-history");
|
||||
let tabList = historyComponent.lists[0];
|
||||
|
|
|
|||
|
|
@ -169,7 +169,7 @@ add_task(async function test_list_ordering() {
|
|||
await withFirefoxView({}, async browser => {
|
||||
const { document } = browser.contentWindow;
|
||||
|
||||
await navigateToCategoryAndWait(document, "history");
|
||||
await navigateToViewAndWait(document, "history");
|
||||
|
||||
let historyComponent = document.querySelector("view-history");
|
||||
historyComponent.profileAge = 8;
|
||||
|
|
@ -262,7 +262,7 @@ add_task(async function test_empty_states() {
|
|||
await withFirefoxView({}, async browser => {
|
||||
const { document } = browser.contentWindow;
|
||||
|
||||
await navigateToCategoryAndWait(document, "history");
|
||||
await navigateToViewAndWait(document, "history");
|
||||
|
||||
let historyComponent = document.querySelector("view-history");
|
||||
historyComponent.profileAge = 8;
|
||||
|
|
@ -350,7 +350,7 @@ add_task(async function test_observers_removed_when_view_is_hidden() {
|
|||
);
|
||||
await withFirefoxView({}, async browser => {
|
||||
const { document } = browser.contentWindow;
|
||||
await navigateToCategoryAndWait(document, "history");
|
||||
await navigateToViewAndWait(document, "history");
|
||||
const historyComponent = document.querySelector("view-history");
|
||||
historyComponent.profileAge = 8;
|
||||
let visitList = await TestUtils.waitForCondition(() =>
|
||||
|
|
@ -399,7 +399,7 @@ add_task(async function test_show_all_history_telemetry() {
|
|||
await withFirefoxView({}, async browser => {
|
||||
const { document } = browser.contentWindow;
|
||||
|
||||
await navigateToCategoryAndWait(document, "history");
|
||||
await navigateToViewAndWait(document, "history");
|
||||
|
||||
let historyComponent = document.querySelector("view-history");
|
||||
historyComponent.profileAge = 8;
|
||||
|
|
@ -424,7 +424,7 @@ add_task(async function test_show_all_history_telemetry() {
|
|||
add_task(async function test_search_history() {
|
||||
await withFirefoxView({}, async browser => {
|
||||
const { document } = browser.contentWindow;
|
||||
await navigateToCategoryAndWait(document, "history");
|
||||
await navigateToViewAndWait(document, "history");
|
||||
const historyComponent = document.querySelector("view-history");
|
||||
historyComponent.profileAge = 8;
|
||||
await historyComponentReady(historyComponent);
|
||||
|
|
@ -504,7 +504,7 @@ add_task(async function test_persist_collapse_card_after_view_change() {
|
|||
await addHistoryItems(today);
|
||||
await withFirefoxView({}, async browser => {
|
||||
const { document } = browser.contentWindow;
|
||||
await navigateToCategoryAndWait(document, "history");
|
||||
await navigateToViewAndWait(document, "history");
|
||||
const historyComponent = document.querySelector("view-history");
|
||||
historyComponent.profileAge = 8;
|
||||
await TestUtils.waitForCondition(
|
||||
|
|
@ -529,8 +529,8 @@ add_task(async function test_persist_collapse_card_after_view_change() {
|
|||
);
|
||||
|
||||
// Switch to a new view and then back to History
|
||||
await navigateToCategoryAndWait(document, "syncedtabs");
|
||||
await navigateToCategoryAndWait(document, "history");
|
||||
await navigateToViewAndWait(document, "syncedtabs");
|
||||
await navigateToViewAndWait(document, "history");
|
||||
|
||||
// Check that first history card is still collapsed after changing view
|
||||
ok(
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ add_setup(function () {
|
|||
async function navigateToOpenTabs(browser) {
|
||||
const document = browser.contentDocument;
|
||||
if (document.querySelector("named-deck").selectedViewName != "opentabs") {
|
||||
await navigateToCategoryAndWait(browser.contentDocument, "opentabs");
|
||||
await navigateToViewAndWait(browser.contentDocument, "opentabs");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -591,7 +591,7 @@ add_task(async function search_open_tabs_recent_browsing() {
|
|||
});
|
||||
await openFirefoxViewTab(window).then(async viewTab => {
|
||||
const browser = viewTab.linkedBrowser;
|
||||
await navigateToCategoryAndWait(browser.contentDocument, "recentbrowsing");
|
||||
await navigateToViewAndWait(browser.contentDocument, "recentbrowsing");
|
||||
const recentBrowsing = browser.contentDocument.querySelector(
|
||||
"view-recentbrowsing"
|
||||
);
|
||||
|
|
|
|||
|
|
@ -122,7 +122,7 @@ async function moreMenuSetup() {
|
|||
await clickFirefoxViewButton(window);
|
||||
const document = window.FirefoxViewHandler.tab.linkedBrowser.contentDocument;
|
||||
|
||||
await navigateToCategoryAndWait(document, "opentabs");
|
||||
await navigateToViewAndWait(document, "opentabs");
|
||||
|
||||
let openTabs = document.querySelector("view-opentabs[name=opentabs]");
|
||||
setSortOption(openTabs, "tabStripOrder");
|
||||
|
|
|
|||
|
|
@ -159,8 +159,9 @@ function getOpenTabsComponent(browser) {
|
|||
async function checkTabList(browser, expected) {
|
||||
const tabsView = getOpenTabsComponent(browser);
|
||||
const openTabsCard = tabsView.shadowRoot.querySelector("view-opentabs-card");
|
||||
await tabsView.getUpdateComplete();
|
||||
await tabsView.updateComplete;
|
||||
const tabList = openTabsCard.shadowRoot.querySelector("fxview-tab-list");
|
||||
await tabList.getUpdateComplete();
|
||||
Assert.ok(tabList, "Found the tab list element");
|
||||
await TestUtils.waitForCondition(() => tabList.rowEls.length);
|
||||
let actual = Array.from(tabList.rowEls).map(row => row.url);
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ add_task(async function test_notification_dot_indicator() {
|
|||
await withFirefoxView({}, async browser => {
|
||||
const { document } = browser.contentWindow;
|
||||
let win = browser.ownerGlobal;
|
||||
await navigateToCategoryAndWait(document, "opentabs");
|
||||
await navigateToViewAndWait(document, "opentabs");
|
||||
// load page that opens prompt when page is hidden
|
||||
let openedTab = await BrowserTestUtils.openNewForegroundTab(
|
||||
gBrowser,
|
||||
|
|
@ -48,7 +48,7 @@ add_task(async function test_notification_dot_indicator() {
|
|||
await openTabs.updateComplete;
|
||||
|
||||
await TestUtils.waitForCondition(
|
||||
() => openTabs.viewCards[0].tabList.rowEls[1].attention,
|
||||
() => openTabs.viewCards[0].tabList.rowEls[0].attention,
|
||||
"The opened tab doesn't have the attention property, so no notification dot is shown."
|
||||
);
|
||||
|
||||
|
|
@ -79,7 +79,7 @@ add_task(async function test_container_indicator() {
|
|||
URLs[0]
|
||||
);
|
||||
|
||||
await navigateToCategoryAndWait(document, "opentabs");
|
||||
await navigateToViewAndWait(document, "opentabs");
|
||||
|
||||
let openTabs = document.querySelector("view-opentabs[name=opentabs]");
|
||||
|
||||
|
|
@ -118,7 +118,7 @@ add_task(async function test_container_indicator() {
|
|||
add_task(async function test_sound_playing_muted_indicator() {
|
||||
await withFirefoxView({}, async browser => {
|
||||
const { document } = browser.contentWindow;
|
||||
await navigateToCategoryAndWait(document, "opentabs");
|
||||
await navigateToViewAndWait(document, "opentabs");
|
||||
|
||||
// Load a page in a container tab
|
||||
let soundTab = await BrowserTestUtils.openNewForegroundTab(
|
||||
|
|
@ -146,7 +146,7 @@ add_task(async function test_sound_playing_muted_indicator() {
|
|||
"The tab list hasn't rendered."
|
||||
);
|
||||
|
||||
let soundPlayingTabElem = openTabs.viewCards[0].tabList.rowEls[1];
|
||||
let soundPlayingTabElem = openTabs.viewCards[0].tabList.rowEls[0];
|
||||
|
||||
await TestUtils.waitForCondition(() => soundPlayingTabElem.soundPlaying);
|
||||
|
||||
|
|
|
|||
|
|
@ -196,7 +196,7 @@ add_task(async function test_initial_closed_tab() {
|
|||
await withFirefoxView({}, async browser => {
|
||||
const { document } = browser.contentWindow;
|
||||
is(document.location.href, getFirefoxViewURL());
|
||||
await navigateToCategoryAndWait(document, "recentlyclosed");
|
||||
await navigateToViewAndWait(document, "recentlyclosed");
|
||||
let { cleanup } = await prepareSingleClosedTab();
|
||||
await switchToFxViewTab(window);
|
||||
let [listItems] = await waitForRecentlyClosedTabsList(document);
|
||||
|
|
@ -220,7 +220,7 @@ add_task(async function test_list_ordering() {
|
|||
await withFirefoxView({}, async browser => {
|
||||
const { document } = browser.contentWindow;
|
||||
await clearAllParentTelemetryEvents();
|
||||
navigateToCategory(document, "recentlyclosed");
|
||||
await navigateToViewAndWait(document, "recentlyclosed");
|
||||
let [cardMainSlotNode, listItems] = await waitForRecentlyClosedTabsList(
|
||||
document
|
||||
);
|
||||
|
|
@ -248,7 +248,7 @@ add_task(async function test_list_updates() {
|
|||
|
||||
await withFirefoxView({}, async browser => {
|
||||
const { document } = browser.contentWindow;
|
||||
navigateToCategory(document, "recentlyclosed");
|
||||
await navigateToViewAndWait(document, "recentlyclosed");
|
||||
|
||||
let [listElem, listItems] = await waitForRecentlyClosedTabsList(document);
|
||||
Assert.deepEqual(
|
||||
|
|
@ -321,7 +321,7 @@ add_task(async function test_restore_tab() {
|
|||
|
||||
await withFirefoxView({}, async browser => {
|
||||
const { document } = browser.contentWindow;
|
||||
navigateToCategory(document, "recentlyclosed");
|
||||
await navigateToViewAndWait(document, "recentlyclosed");
|
||||
|
||||
let [listElem, listItems] = await waitForRecentlyClosedTabsList(document);
|
||||
Assert.deepEqual(
|
||||
|
|
@ -365,7 +365,7 @@ add_task(async function test_dismiss_tab() {
|
|||
|
||||
await withFirefoxView({}, async browser => {
|
||||
const { document } = browser.contentWindow;
|
||||
navigateToCategory(document, "recentlyclosed");
|
||||
await navigateToViewAndWait(document, "recentlyclosed");
|
||||
|
||||
let [listElem, listItems] = await waitForRecentlyClosedTabsList(document);
|
||||
await clearAllParentTelemetryEvents();
|
||||
|
|
@ -429,7 +429,7 @@ add_task(async function test_empty_states() {
|
|||
const { document } = browser.contentWindow;
|
||||
is(document.location.href, "about:firefoxview");
|
||||
|
||||
navigateToCategory(document, "recentlyclosed");
|
||||
await navigateToViewAndWait(document, "recentlyclosed");
|
||||
let recentlyClosedComponent = document.querySelector(
|
||||
"view-recentlyclosed:not([slot=recentlyclosed])"
|
||||
);
|
||||
|
|
@ -479,7 +479,7 @@ add_task(async function test_observers_removed_when_view_is_hidden() {
|
|||
|
||||
await withFirefoxView({}, async function (browser) {
|
||||
const { document } = browser.contentWindow;
|
||||
navigateToCategory(document, "recentlyclosed");
|
||||
await navigateToViewAndWait(document, "recentlyclosed");
|
||||
const [listElem] = await waitForRecentlyClosedTabsList(document);
|
||||
is(listElem.rowEls.length, 1);
|
||||
|
||||
|
|
@ -510,7 +510,7 @@ add_task(async function test_search() {
|
|||
let { cleanup, expectedURLs } = await prepareClosedTabs();
|
||||
await withFirefoxView({}, async browser => {
|
||||
const { document } = browser.contentWindow;
|
||||
navigateToCategory(document, "recentlyclosed");
|
||||
await navigateToViewAndWait(document, "recentlyclosed");
|
||||
const [listElem] = await waitForRecentlyClosedTabsList(document);
|
||||
const recentlyClosedComponent = document.querySelector(
|
||||
"view-recentlyclosed:not([slot=recentlyclosed])"
|
||||
|
|
@ -569,7 +569,7 @@ add_task(async function test_search_recent_browsing() {
|
|||
const { document } = browser.contentWindow;
|
||||
|
||||
info("Input a search query.");
|
||||
await navigateToCategoryAndWait(document, "recentbrowsing");
|
||||
await navigateToViewAndWait(document, "recentbrowsing");
|
||||
const recentBrowsing = document.querySelector("view-recentbrowsing");
|
||||
EventUtils.synthesizeMouseAtCenter(
|
||||
recentBrowsing.searchTextbox,
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@ add_task(async function test_network_offline() {
|
|||
sandbox.spy(TabsSetupFlowManager, "tryToClearError");
|
||||
await withFirefoxView({}, async browser => {
|
||||
const { document } = browser.contentWindow;
|
||||
await navigateToCategoryAndWait(document, "syncedtabs");
|
||||
await navigateToViewAndWait(document, "syncedtabs");
|
||||
|
||||
Services.obs.notifyObservers(null, UIState.ON_UPDATE);
|
||||
Services.obs.notifyObservers(
|
||||
|
|
@ -112,7 +112,7 @@ add_task(async function test_sync_error() {
|
|||
const sandbox = await setupWithDesktopDevices();
|
||||
await withFirefoxView({}, async browser => {
|
||||
const { document } = browser.contentWindow;
|
||||
await navigateToCategoryAndWait(document, "syncedtabs");
|
||||
await navigateToViewAndWait(document, "syncedtabs");
|
||||
|
||||
Services.obs.notifyObservers(null, UIState.ON_UPDATE);
|
||||
Services.obs.notifyObservers(null, "weave:service:sync:error");
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ add_task(async function test_unconfigured_initial_state() {
|
|||
});
|
||||
await withFirefoxView({}, async browser => {
|
||||
const { document } = browser.contentWindow;
|
||||
await navigateToCategoryAndWait(document, "syncedtabs");
|
||||
await navigateToViewAndWait(document, "syncedtabs");
|
||||
Services.obs.notifyObservers(null, UIState.ON_UPDATE);
|
||||
|
||||
let syncedTabsComponent = document.querySelector(
|
||||
|
|
@ -85,7 +85,7 @@ add_task(async function test_signed_in() {
|
|||
|
||||
await withFirefoxView({}, async browser => {
|
||||
const { document } = browser.contentWindow;
|
||||
await navigateToCategoryAndWait(document, "syncedtabs");
|
||||
await navigateToViewAndWait(document, "syncedtabs");
|
||||
Services.obs.notifyObservers(null, UIState.ON_UPDATE);
|
||||
|
||||
let syncedTabsComponent = document.querySelector(
|
||||
|
|
@ -148,7 +148,7 @@ add_task(async function test_no_synced_tabs() {
|
|||
|
||||
await withFirefoxView({}, async browser => {
|
||||
const { document } = browser.contentWindow;
|
||||
await navigateToCategoryAndWait(document, "syncedtabs");
|
||||
await navigateToViewAndWait(document, "syncedtabs");
|
||||
Services.obs.notifyObservers(null, UIState.ON_UPDATE);
|
||||
|
||||
let syncedTabsComponent = document.querySelector(
|
||||
|
|
@ -188,7 +188,7 @@ add_task(async function test_no_error_for_two_desktop() {
|
|||
|
||||
await withFirefoxView({}, async browser => {
|
||||
const { document } = browser.contentWindow;
|
||||
await navigateToCategoryAndWait(document, "syncedtabs");
|
||||
await navigateToViewAndWait(document, "syncedtabs");
|
||||
Services.obs.notifyObservers(null, UIState.ON_UPDATE);
|
||||
|
||||
let syncedTabsComponent = document.querySelector(
|
||||
|
|
@ -232,7 +232,7 @@ add_task(async function test_empty_state() {
|
|||
|
||||
await withFirefoxView({ openNewWindow: true }, async browser => {
|
||||
const { document } = browser.contentWindow;
|
||||
await navigateToCategoryAndWait(document, "syncedtabs");
|
||||
await navigateToViewAndWait(document, "syncedtabs");
|
||||
Services.obs.notifyObservers(null, UIState.ON_UPDATE);
|
||||
|
||||
let syncedTabsComponent = document.querySelector(
|
||||
|
|
@ -277,7 +277,7 @@ add_task(async function test_tabs() {
|
|||
|
||||
await withFirefoxView({ openNewWindow: true }, async browser => {
|
||||
const { document } = browser.contentWindow;
|
||||
await navigateToCategoryAndWait(document, "syncedtabs");
|
||||
await navigateToViewAndWait(document, "syncedtabs");
|
||||
Services.obs.notifyObservers(null, UIState.ON_UPDATE);
|
||||
|
||||
let syncedTabsComponent = document.querySelector(
|
||||
|
|
@ -365,7 +365,7 @@ add_task(async function test_empty_desktop_same_name() {
|
|||
|
||||
await withFirefoxView({ openNewWindow: true }, async browser => {
|
||||
const { document } = browser.contentWindow;
|
||||
await navigateToCategoryAndWait(document, "syncedtabs");
|
||||
await navigateToViewAndWait(document, "syncedtabs");
|
||||
Services.obs.notifyObservers(null, UIState.ON_UPDATE);
|
||||
|
||||
let syncedTabsComponent = document.querySelector(
|
||||
|
|
@ -413,7 +413,7 @@ add_task(async function test_empty_desktop_same_name_three() {
|
|||
|
||||
await withFirefoxView({ openNewWindow: true }, async browser => {
|
||||
const { document } = browser.contentWindow;
|
||||
await navigateToCategoryAndWait(document, "syncedtabs");
|
||||
await navigateToViewAndWait(document, "syncedtabs");
|
||||
Services.obs.notifyObservers(null, UIState.ON_UPDATE);
|
||||
|
||||
let syncedTabsComponent = document.querySelector(
|
||||
|
|
@ -459,7 +459,7 @@ add_task(async function search_synced_tabs() {
|
|||
|
||||
await withFirefoxView({}, async browser => {
|
||||
const { document } = browser.contentWindow;
|
||||
await navigateToCategoryAndWait(document, "syncedtabs");
|
||||
await navigateToViewAndWait(document, "syncedtabs");
|
||||
Services.obs.notifyObservers(null, UIState.ON_UPDATE);
|
||||
|
||||
let syncedTabsComponent = document.querySelector(
|
||||
|
|
@ -666,7 +666,7 @@ add_task(async function search_synced_tabs_recent_browsing() {
|
|||
});
|
||||
await withFirefoxView({}, async browser => {
|
||||
const { document } = browser.contentWindow;
|
||||
await navigateToCategoryAndWait(document, "recentbrowsing");
|
||||
await navigateToViewAndWait(document, "recentbrowsing");
|
||||
Services.obs.notifyObservers(null, UIState.ON_UPDATE);
|
||||
|
||||
const recentBrowsing = document.querySelector("view-recentbrowsing");
|
||||
|
|
|
|||
|
|
@ -548,31 +548,19 @@ registerCleanupFunction(() => {
|
|||
gSandbox?.restore();
|
||||
});
|
||||
|
||||
function navigateToCategory(document, category) {
|
||||
const navigation = document.querySelector("fxview-category-navigation");
|
||||
let navButton = Array.from(navigation.categoryButtons).filter(
|
||||
categoryButton => {
|
||||
return categoryButton.name === category;
|
||||
}
|
||||
)[0];
|
||||
navButton.buttonEl.click();
|
||||
}
|
||||
|
||||
async function navigateToCategoryAndWait(document, category) {
|
||||
info(`navigateToCategoryAndWait, for ${category}`);
|
||||
const navigation = document.querySelector("fxview-category-navigation");
|
||||
async function navigateToViewAndWait(document, view) {
|
||||
info(`navigateToViewAndWait, for ${view}`);
|
||||
const navigation = document.querySelector("moz-page-nav");
|
||||
const win = document.ownerGlobal;
|
||||
SimpleTest.promiseFocus(win);
|
||||
let navButton = Array.from(navigation.categoryButtons).find(
|
||||
categoryButton => {
|
||||
return categoryButton.name === category;
|
||||
}
|
||||
);
|
||||
let navButton = Array.from(navigation.pageNavButtons).find(pageNavButton => {
|
||||
return pageNavButton.view === view;
|
||||
});
|
||||
const namedDeck = document.querySelector("named-deck");
|
||||
|
||||
await BrowserTestUtils.waitForCondition(
|
||||
() => navButton.getBoundingClientRect().height,
|
||||
`Waiting for ${category} button to be clickable`
|
||||
`Waiting for ${view} button to be clickable`
|
||||
);
|
||||
|
||||
EventUtils.synthesizeMouseAtCenter(navButton, {}, win);
|
||||
|
|
@ -582,10 +570,10 @@ async function navigateToCategoryAndWait(document, category) {
|
|||
child => child.slot == "selected"
|
||||
);
|
||||
return (
|
||||
namedDeck.selectedViewName == category &&
|
||||
namedDeck.selectedViewName == view &&
|
||||
selectedView?.getBoundingClientRect().height
|
||||
);
|
||||
}, `Waiting for ${category} to be visible`);
|
||||
}, `Waiting for ${view} to be visible`);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -2,6 +2,4 @@
|
|||
|
||||
["test_card_container.html"]
|
||||
|
||||
["test_fxview_category_navigation.html"]
|
||||
|
||||
["test_fxview_tab_list.html"]
|
||||
|
|
|
|||
|
|
@ -1,322 +0,0 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>FxviewCategoryNavigation Tests</title>
|
||||
<script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
|
||||
<link rel="stylesheet" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
|
||||
<link rel="stylesheet" href="chrome://global/skin/in-content/common.css">
|
||||
<script type="module" src="chrome://browser/content/firefoxview/fxview-category-navigation.mjs"></script>
|
||||
</head>
|
||||
<style>
|
||||
body {
|
||||
display: flex;
|
||||
}
|
||||
#navigation {
|
||||
width: var(--in-content-sidebar-width);
|
||||
}
|
||||
fxview-category-button[name="category-one"]::part(icon) {
|
||||
background-image: url("chrome://mozapps/skin/extensions/category-discover.svg");
|
||||
}
|
||||
fxview-category-button[name="category-two"]::part(icon) {
|
||||
background-image: url("chrome://mozapps/skin/extensions/category-discover.svg");
|
||||
}
|
||||
fxview-category-button[name="category-three"]::part(icon) {
|
||||
background-image: url("chrome://mozapps/skin/extensions/category-discover.svg");
|
||||
}
|
||||
fxview-category-button[name="category-four"]::part(icon) {
|
||||
background-image: url("chrome://mozapps/skin/extensions/category-discover.svg");
|
||||
}
|
||||
fxview-category-button[name="category-five"]::part(icon) {
|
||||
background-image: url("chrome://mozapps/skin/extensions/category-discover.svg");
|
||||
}
|
||||
</style>
|
||||
<body>
|
||||
<p id="display"></p>
|
||||
<div id="content">
|
||||
<div id="navigation">
|
||||
<fxview-category-navigation>
|
||||
<h2 slot="category-nav-header">Header</h2>
|
||||
<fxview-category-button class="category" slot="category-button" name="category-one">
|
||||
<span class="category-name">Category 1</span>
|
||||
</fxview-category-button>
|
||||
<fxview-category-button class="category" slot="category-button" name="category-two">
|
||||
<span class="category-name">Category 2</span>
|
||||
</fxview-category-button>
|
||||
<fxview-category-button class="category" slot="category-button" name="category-three">
|
||||
<span class="category-name">Category 3</span>
|
||||
</fxview-category-button>
|
||||
<fxview-category-button class="category" slot="category-button" name="category-four">
|
||||
<span class="category-name">Category 4</span>
|
||||
</fxview-category-button>
|
||||
<fxview-category-button class="category" slot="category-button" name="category-five">
|
||||
<span class="category-name">Category 5</span>
|
||||
</fxview-category-button>
|
||||
</fxview-category-navigation>
|
||||
</div>
|
||||
</div>
|
||||
<pre id="test"></pre>
|
||||
<script>
|
||||
Services.scriptloader.loadSubScript(
|
||||
"chrome://browser/content/utilityOverlay.js",
|
||||
this
|
||||
);
|
||||
const { BrowserTestUtils } = ChromeUtils.importESModule(
|
||||
"resource://testing-common/BrowserTestUtils.sys.mjs"
|
||||
);
|
||||
|
||||
const fxviewCategoryNav = document.querySelector("fxview-category-navigation");
|
||||
|
||||
function isActiveElement(expectedActiveEl) {
|
||||
return expectedActiveEl.getRootNode().activeElement == expectedActiveEl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that the first category is selected by default
|
||||
*/
|
||||
add_task(async function test_first_item_selected_by_default() {
|
||||
is(
|
||||
fxviewCategoryNav.categoryButtons.length,
|
||||
5,
|
||||
"Five category buttons are in the navigation"
|
||||
);
|
||||
|
||||
ok(
|
||||
fxviewCategoryNav.categoryButtons[0].name === fxviewCategoryNav.currentCategory,
|
||||
"The first category button is selected by default"
|
||||
)
|
||||
});
|
||||
|
||||
/**
|
||||
* Tests that categories are selected when clicked
|
||||
*/
|
||||
add_task(async function test_select_category() {
|
||||
let gBrowser = BrowserWindowTracker.getTopWindow().top.gBrowser;
|
||||
let secondCategory = fxviewCategoryNav.categoryButtons[1];
|
||||
let categoryChanged = BrowserTestUtils.waitForEvent(
|
||||
gBrowser,
|
||||
"change-category"
|
||||
);
|
||||
|
||||
secondCategory.buttonEl.click();
|
||||
await categoryChanged;
|
||||
|
||||
ok(
|
||||
secondCategory.name === fxviewCategoryNav.currentCategory,
|
||||
"The second category button is selected"
|
||||
)
|
||||
|
||||
let thirdCategory = fxviewCategoryNav.categoryButtons[2];
|
||||
categoryChanged = BrowserTestUtils.waitForEvent(
|
||||
gBrowser,
|
||||
"change-category"
|
||||
);
|
||||
|
||||
thirdCategory.buttonEl.click();
|
||||
await categoryChanged;
|
||||
|
||||
ok(
|
||||
thirdCategory.name === fxviewCategoryNav.currentCategory,
|
||||
"The third category button is selected"
|
||||
)
|
||||
|
||||
let firstCategory = fxviewCategoryNav.categoryButtons[0];
|
||||
categoryChanged = BrowserTestUtils.waitForEvent(
|
||||
gBrowser,
|
||||
"change-category"
|
||||
);
|
||||
|
||||
firstCategory.buttonEl.click();
|
||||
await categoryChanged;
|
||||
|
||||
ok(
|
||||
firstCategory.name === fxviewCategoryNav.currentCategory,
|
||||
"The first category button is selected"
|
||||
)
|
||||
});
|
||||
|
||||
/**
|
||||
* Tests that categories are keyboard-navigable
|
||||
*/
|
||||
add_task(async function test_keyboard_navigation() {
|
||||
const arrowDown = async () => {
|
||||
info("Arrow down");
|
||||
synthesizeKey("KEY_ArrowDown", {});
|
||||
await fxviewCategoryNav.getUpdateComplete();
|
||||
};
|
||||
const arrowUp = async () => {
|
||||
info("Arrow up");
|
||||
synthesizeKey("KEY_ArrowUp", {});
|
||||
await fxviewCategoryNav.getUpdateComplete();
|
||||
};
|
||||
const arrowLeft = async () => {
|
||||
info("Arrow left");
|
||||
synthesizeKey("KEY_ArrowLeft", {});
|
||||
await fxviewCategoryNav.getUpdateComplete();
|
||||
};
|
||||
const arrowRight = async () => {
|
||||
info("Arrow right");
|
||||
synthesizeKey("KEY_ArrowRight", {});
|
||||
await fxviewCategoryNav.getUpdateComplete();
|
||||
};
|
||||
|
||||
// Setting this pref allows the test to run as expected with a keyboard on MacOS
|
||||
await SpecialPowers.pushPrefEnv({
|
||||
set: [["accessibility.tabfocus", 7]],
|
||||
});
|
||||
|
||||
let firstCategory = fxviewCategoryNav.categoryButtons[0];
|
||||
let secondCategory = fxviewCategoryNav.categoryButtons[1];
|
||||
let thirdCategory = fxviewCategoryNav.categoryButtons[2];
|
||||
let fourthCategory = fxviewCategoryNav.categoryButtons[3];
|
||||
let fifthCategory = fxviewCategoryNav.categoryButtons[4];
|
||||
|
||||
is(
|
||||
firstCategory.name,
|
||||
fxviewCategoryNav.currentCategory,
|
||||
"The first category button is selected"
|
||||
)
|
||||
firstCategory.focus();
|
||||
await arrowDown();
|
||||
ok(
|
||||
isActiveElement(secondCategory),
|
||||
"The second category button is the active element after first arrow down"
|
||||
);
|
||||
is(
|
||||
secondCategory.name,
|
||||
fxviewCategoryNav.currentCategory,
|
||||
"The second category button is selected"
|
||||
)
|
||||
await arrowDown();
|
||||
is(
|
||||
thirdCategory.name,
|
||||
fxviewCategoryNav.currentCategory,
|
||||
"The third category button is selected"
|
||||
)
|
||||
await arrowDown();
|
||||
is(
|
||||
fourthCategory.name,
|
||||
fxviewCategoryNav.currentCategory,
|
||||
"The fourth category button is selected"
|
||||
)
|
||||
await arrowDown();
|
||||
is(
|
||||
fifthCategory.name,
|
||||
fxviewCategoryNav.currentCategory,
|
||||
"The fifth category button is selected"
|
||||
)
|
||||
await arrowDown();
|
||||
is(
|
||||
fifthCategory.name,
|
||||
fxviewCategoryNav.currentCategory,
|
||||
"The fifth category button is still selected"
|
||||
)
|
||||
await arrowUp();
|
||||
is(
|
||||
fourthCategory.name,
|
||||
fxviewCategoryNav.currentCategory,
|
||||
"The fourth category button is selected"
|
||||
)
|
||||
await arrowUp();
|
||||
is(
|
||||
thirdCategory.name,
|
||||
fxviewCategoryNav.currentCategory,
|
||||
"The third category button is selected"
|
||||
)
|
||||
await arrowUp();
|
||||
is(
|
||||
secondCategory.name,
|
||||
fxviewCategoryNav.currentCategory,
|
||||
"The second category button is selected"
|
||||
)
|
||||
await arrowUp();
|
||||
is(
|
||||
firstCategory.name,
|
||||
fxviewCategoryNav.currentCategory,
|
||||
"The first category button is selected"
|
||||
)
|
||||
await arrowUp();
|
||||
is(
|
||||
firstCategory.name,
|
||||
fxviewCategoryNav.currentCategory,
|
||||
"The first category button is still selected"
|
||||
)
|
||||
|
||||
// Test navigation with arrow left/right keys
|
||||
is(
|
||||
firstCategory.name,
|
||||
fxviewCategoryNav.currentCategory,
|
||||
"The first category button is selected"
|
||||
)
|
||||
firstCategory.focus();
|
||||
await arrowRight();
|
||||
ok(
|
||||
isActiveElement(secondCategory),
|
||||
"The second category button is the active element after first arrow right"
|
||||
);
|
||||
is(
|
||||
secondCategory.name,
|
||||
fxviewCategoryNav.currentCategory,
|
||||
"The second category button is selected"
|
||||
)
|
||||
await arrowRight();
|
||||
is(
|
||||
thirdCategory.name,
|
||||
fxviewCategoryNav.currentCategory,
|
||||
"The third category button is selected"
|
||||
)
|
||||
await arrowRight();
|
||||
is(
|
||||
fourthCategory.name,
|
||||
fxviewCategoryNav.currentCategory,
|
||||
"The fourth category button is selected"
|
||||
)
|
||||
await arrowRight();
|
||||
is(
|
||||
fifthCategory.name,
|
||||
fxviewCategoryNav.currentCategory,
|
||||
"The fifth category button is selected"
|
||||
)
|
||||
await arrowRight();
|
||||
is(
|
||||
fifthCategory.name,
|
||||
fxviewCategoryNav.currentCategory,
|
||||
"The fifth category button is still selected"
|
||||
)
|
||||
await arrowLeft();
|
||||
is(
|
||||
fourthCategory.name,
|
||||
fxviewCategoryNav.currentCategory,
|
||||
"The fourth category button is selected"
|
||||
)
|
||||
await arrowLeft();
|
||||
is(
|
||||
thirdCategory.name,
|
||||
fxviewCategoryNav.currentCategory,
|
||||
"The third category button is selected"
|
||||
)
|
||||
await arrowLeft();
|
||||
is(
|
||||
secondCategory.name,
|
||||
fxviewCategoryNav.currentCategory,
|
||||
"The second category button is selected"
|
||||
)
|
||||
await arrowLeft();
|
||||
is(
|
||||
firstCategory.name,
|
||||
fxviewCategoryNav.currentCategory,
|
||||
"The first category button is selected"
|
||||
)
|
||||
await arrowLeft();
|
||||
is(
|
||||
firstCategory.name,
|
||||
fxviewCategoryNav.currentCategory,
|
||||
"The first category button is still selected"
|
||||
)
|
||||
|
||||
await SpecialPowers.popPrefEnv();
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -1,113 +0,0 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
import { html } from "lit.all.mjs";
|
||||
// eslint-disable-next-line import/no-unassigned-import
|
||||
import "browser/components/firefoxview/fxview-category-navigation.mjs";
|
||||
|
||||
export default {
|
||||
title: "Domain-specific UI Widgets/Firefox View/Category Navigation",
|
||||
component: "fxview-category-navigation",
|
||||
parameters: {
|
||||
status: "in-development",
|
||||
actions: {
|
||||
handles: ["change-category"],
|
||||
},
|
||||
fluent: `
|
||||
fxview-category-button-one = Category 1
|
||||
.title = Category 1
|
||||
fxview-category-button-two = Category 2
|
||||
.title = Category 2
|
||||
fxview-category-button-three = Category 3
|
||||
.title = Category 3
|
||||
fxview-category-footer-button = Settings
|
||||
.title = Settings
|
||||
`,
|
||||
},
|
||||
};
|
||||
|
||||
const Template = () => html`
|
||||
<style>
|
||||
#page {
|
||||
height: 100%;
|
||||
display: grid;
|
||||
grid-template-columns: var(--in-content-sidebar-width) 1fr;
|
||||
}
|
||||
fxview-category-navigation {
|
||||
margin-inline-start: 10px;
|
||||
}
|
||||
fxview-category-button[name="category-one"]::part(icon) {
|
||||
background-image: url("chrome://browser/skin/preferences/category-general.svg");
|
||||
}
|
||||
fxview-category-button[name="category-two"]::part(icon) {
|
||||
background-image: url("chrome://browser/skin/preferences/category-general.svg");
|
||||
}
|
||||
fxview-category-button[name="category-three"]::part(icon) {
|
||||
background-image: url("chrome://browser/skin/preferences/category-general.svg");
|
||||
}
|
||||
.footer-button {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
font-weight: normal;
|
||||
min-width: unset;
|
||||
padding: 8px;
|
||||
margin: 0;
|
||||
}
|
||||
.footer-button .cat-icon {
|
||||
background-image: url("chrome://browser/skin/preferences/category-general.svg");
|
||||
background-color: initial;
|
||||
background-size: 20px;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
height: 20px;
|
||||
width: 20px;
|
||||
display: inline-block;
|
||||
-moz-context-properties: fill;
|
||||
fill: currentColor;
|
||||
}
|
||||
@media (max-width: 52rem) {
|
||||
#page {
|
||||
grid-template-columns: 82px 1fr;
|
||||
}
|
||||
.cat-name {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<div id="page">
|
||||
<fxview-category-navigation>
|
||||
<h2 slot="category-nav-header">Header</h2>
|
||||
<fxview-category-button
|
||||
slot="category-button"
|
||||
name="category-one"
|
||||
data-l10n-id="fxview-category-button-one"
|
||||
>
|
||||
</fxview-category-button>
|
||||
<fxview-category-button
|
||||
slot="category-button"
|
||||
name="category-two"
|
||||
data-l10n-id="fxview-category-button-two"
|
||||
>
|
||||
</fxview-category-button>
|
||||
<fxview-category-button
|
||||
slot="category-button"
|
||||
name="category-three"
|
||||
data-l10n-id="fxview-category-button-three"
|
||||
>
|
||||
</fxview-category-button>
|
||||
<div slot="category-nav-footer" class="category-nav-footer">
|
||||
<button class="footer-button ghost-button">
|
||||
<span class="cat-icon"></span>
|
||||
<span
|
||||
class="cat-name"
|
||||
data-l10n-id="fxview-category-footer-button"
|
||||
></span>
|
||||
</button>
|
||||
</div>
|
||||
</fxview-category-navigation>
|
||||
</div>
|
||||
`;
|
||||
|
||||
export const Default = Template.bind({});
|
||||
Default.args = {};
|
||||
|
|
@ -12,6 +12,9 @@ menu-tools-firefox-view =
|
|||
|
||||
firefoxview-page-title = { -firefoxview-brand-name }
|
||||
|
||||
firefoxview-page-heading =
|
||||
.heading = { -firefoxview-brand-name }
|
||||
|
||||
firefoxview-page-label =
|
||||
.label = { -firefoxview-brand-name }
|
||||
|
||||
|
|
|
|||
|
|
@ -95,6 +95,9 @@ toolkit.jar:
|
|||
content/global/elements/moz-button-group.mjs (widgets/moz-button-group/moz-button-group.mjs)
|
||||
content/global/elements/moz-card.css (widgets/moz-card/moz-card.css)
|
||||
content/global/elements/moz-card.mjs (widgets/moz-card/moz-card.mjs)
|
||||
content/global/elements/moz-page-nav.css (widgets/moz-page-nav/moz-page-nav.css)
|
||||
content/global/elements/moz-page-nav-button.css (widgets/moz-page-nav/moz-page-nav-button.css)
|
||||
content/global/elements/moz-page-nav.mjs (widgets/moz-page-nav/moz-page-nav.mjs)
|
||||
content/global/elements/moz-five-star.css (widgets/moz-five-star/moz-five-star.css)
|
||||
content/global/elements/moz-five-star.mjs (widgets/moz-five-star/moz-five-star.mjs)
|
||||
content/global/elements/moz-input-box.js (widgets/moz-input-box.js)
|
||||
|
|
|
|||
|
|
@ -32,6 +32,8 @@ skip-if = ["os == 'mac'"]
|
|||
|
||||
["test_moz_message_bar.html"]
|
||||
|
||||
["test_moz_page_nav.html"]
|
||||
|
||||
["test_moz_support_link.html"]
|
||||
|
||||
["test_moz_toggle.html"]
|
||||
|
|
@ -65,4 +67,5 @@ skip-if = [
|
|||
"os == 'android'",
|
||||
"os == 'linux' && debug", # Bug 1765783
|
||||
]
|
||||
|
||||
["test_videocontrols_onclickplay.html"]
|
||||
|
|
|
|||
306
toolkit/content/tests/widgets/test_moz_page_nav.html
Normal file
|
|
@ -0,0 +1,306 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>MozPageNav Tests</title>
|
||||
<script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
|
||||
<link rel="stylesheet" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
|
||||
<link rel="stylesheet" href="chrome://global/skin/in-content/common.css">
|
||||
<script type="module" src="chrome://global/content/elements/moz-page-nav.mjs"></script>
|
||||
</head>
|
||||
<style>
|
||||
body {
|
||||
display: flex;
|
||||
}
|
||||
#navigation {
|
||||
width: var(--page-nav-width);
|
||||
}
|
||||
</style>
|
||||
<body>
|
||||
<p id="display"></p>
|
||||
<div id="content">
|
||||
<div id="navigation">
|
||||
<moz-page-nav heading="Heading">
|
||||
<moz-page-nav-button view="view-one" iconSrc="chrome://mozapps/skin/extensions/category-discover.svg">
|
||||
<span class="view-name">View 1</span>
|
||||
</moz-page-nav-button>
|
||||
<moz-page-nav-button view="view-two" iconSrc="chrome://mozapps/skin/extensions/category-discover.svg">
|
||||
<span class="view-name">View 2</span>
|
||||
</moz-page-nav-button>
|
||||
<moz-page-nav-button view="view-three" iconSrc="chrome://mozapps/skin/extensions/category-discover.svg">
|
||||
<span class="view-name">View 3</span>
|
||||
</moz-page-nav-button>
|
||||
<moz-page-nav-button view="view-four" iconSrc="chrome://mozapps/skin/extensions/category-discover.svg">
|
||||
<span class="view-name">View 4</span>
|
||||
</moz-page-nav-button>
|
||||
<moz-page-nav-button view="view-five" iconSrc="chrome://mozapps/skin/extensions/category-discover.svg">
|
||||
<span class="view-name">View 5</span>
|
||||
</moz-page-nav-button>
|
||||
</moz-page-nav>
|
||||
</div>
|
||||
</div>
|
||||
<pre id="test"></pre>
|
||||
<script>
|
||||
Services.scriptloader.loadSubScript(
|
||||
"chrome://browser/content/utilityOverlay.js",
|
||||
this
|
||||
);
|
||||
const { BrowserTestUtils } = ChromeUtils.importESModule(
|
||||
"resource://testing-common/BrowserTestUtils.sys.mjs"
|
||||
);
|
||||
|
||||
const mozPageNav = document.querySelector("moz-page-nav");
|
||||
|
||||
function isActiveElement(expectedActiveEl) {
|
||||
return expectedActiveEl.getRootNode().activeElement == expectedActiveEl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that the first page nav button is selected by default
|
||||
*/
|
||||
add_task(async function test_first_item_selected_by_default() {
|
||||
is(
|
||||
mozPageNav.pageNavButtons.length,
|
||||
5,
|
||||
"Five page nav buttons are in the navigation"
|
||||
);
|
||||
|
||||
ok(
|
||||
mozPageNav.pageNavButtons[0].view === mozPageNav.currentView,
|
||||
"The first page nav button is selected by default"
|
||||
)
|
||||
});
|
||||
|
||||
/**
|
||||
* Tests that views are selected when clicked
|
||||
*/
|
||||
add_task(async function test_select_view() {
|
||||
let gBrowser = BrowserWindowTracker.getTopWindow().top.gBrowser;
|
||||
let secondViewButton = mozPageNav.pageNavButtons[1];
|
||||
let viewChanged = BrowserTestUtils.waitForEvent(
|
||||
gBrowser,
|
||||
"change-view"
|
||||
);
|
||||
|
||||
secondViewButton.buttonEl.click();
|
||||
await viewChanged;
|
||||
|
||||
ok(
|
||||
secondViewButton.view === mozPageNav.currentView,
|
||||
"The second page nav button is selected"
|
||||
)
|
||||
|
||||
let thirdPageNavButton = mozPageNav.pageNavButtons[2];
|
||||
viewChanged = BrowserTestUtils.waitForEvent(
|
||||
gBrowser,
|
||||
"change-view"
|
||||
);
|
||||
|
||||
thirdPageNavButton.buttonEl.click();
|
||||
await viewChanged;
|
||||
|
||||
ok(
|
||||
thirdPageNavButton.view === mozPageNav.currentView,
|
||||
"The third page nav button is selected"
|
||||
)
|
||||
|
||||
let firstPageNavButton = mozPageNav.pageNavButtons[0];
|
||||
viewChanged = BrowserTestUtils.waitForEvent(
|
||||
gBrowser,
|
||||
"change-view"
|
||||
);
|
||||
|
||||
firstPageNavButton.buttonEl.click();
|
||||
await viewChanged;
|
||||
|
||||
ok(
|
||||
firstPageNavButton.view === mozPageNav.currentView,
|
||||
"The first page nav button is selected"
|
||||
)
|
||||
});
|
||||
|
||||
/**
|
||||
* Tests that categories are keyboard-navigable
|
||||
*/
|
||||
add_task(async function test_keyboard_navigation() {
|
||||
const arrowDown = async () => {
|
||||
info("Arrow down");
|
||||
synthesizeKey("KEY_ArrowDown", {});
|
||||
await mozPageNav.updateComplete;
|
||||
};
|
||||
const arrowUp = async () => {
|
||||
info("Arrow up");
|
||||
synthesizeKey("KEY_ArrowUp", {});
|
||||
await mozPageNav.updateComplete;
|
||||
};
|
||||
const arrowLeft = async () => {
|
||||
info("Arrow left");
|
||||
synthesizeKey("KEY_ArrowLeft", {});
|
||||
await mozPageNav.updateComplete;
|
||||
};
|
||||
const arrowRight = async () => {
|
||||
info("Arrow right");
|
||||
synthesizeKey("KEY_ArrowRight", {});
|
||||
await mozPageNav.updateComplete;
|
||||
};
|
||||
|
||||
// Setting this pref allows the test to run as expected with a keyboard on MacOS
|
||||
await SpecialPowers.pushPrefEnv({
|
||||
set: [["accessibility.tabfocus", 7]],
|
||||
});
|
||||
|
||||
let firstPageNavButton = mozPageNav.pageNavButtons[0];
|
||||
let secondPageNavButton = mozPageNav.pageNavButtons[1];
|
||||
let thirdPageNavButton = mozPageNav.pageNavButtons[2];
|
||||
let fourthPageNavButton = mozPageNav.pageNavButtons[3];
|
||||
let fifthPageNavButton = mozPageNav.pageNavButtons[4];
|
||||
|
||||
is(
|
||||
firstPageNavButton.view,
|
||||
mozPageNav.currentView,
|
||||
"The first page nav button is selected"
|
||||
)
|
||||
firstPageNavButton.buttonEl.focus();
|
||||
await arrowDown();
|
||||
ok(
|
||||
isActiveElement(secondPageNavButton),
|
||||
"The second page nav button is the active element after first arrow down"
|
||||
);
|
||||
is(
|
||||
secondPageNavButton.view,
|
||||
mozPageNav.currentView,
|
||||
"The second page nav button is selected"
|
||||
)
|
||||
await arrowDown();
|
||||
is(
|
||||
thirdPageNavButton.view,
|
||||
mozPageNav.currentView,
|
||||
"The third page nav button is selected"
|
||||
)
|
||||
await arrowDown();
|
||||
is(
|
||||
fourthPageNavButton.view,
|
||||
mozPageNav.currentView,
|
||||
"The fourth page nav button is selected"
|
||||
)
|
||||
await arrowDown();
|
||||
is(
|
||||
fifthPageNavButton.view,
|
||||
mozPageNav.currentView,
|
||||
"The fifth page nav button is selected"
|
||||
)
|
||||
await arrowDown();
|
||||
is(
|
||||
fifthPageNavButton.view,
|
||||
mozPageNav.currentView,
|
||||
"The fifth page nav button is still selected"
|
||||
)
|
||||
await arrowUp();
|
||||
is(
|
||||
fourthPageNavButton.view,
|
||||
mozPageNav.currentView,
|
||||
"The fourth page nav button is selected"
|
||||
)
|
||||
await arrowUp();
|
||||
is(
|
||||
thirdPageNavButton.view,
|
||||
mozPageNav.currentView,
|
||||
"The third page nav button is selected"
|
||||
)
|
||||
await arrowUp();
|
||||
is(
|
||||
secondPageNavButton.view,
|
||||
mozPageNav.currentView,
|
||||
"The second page nav button is selected"
|
||||
)
|
||||
await arrowUp();
|
||||
is(
|
||||
firstPageNavButton.view,
|
||||
mozPageNav.currentView,
|
||||
"The first page nav button is selected"
|
||||
)
|
||||
await arrowUp();
|
||||
is(
|
||||
firstPageNavButton.view,
|
||||
mozPageNav.currentView,
|
||||
"The first page nav button is still selected"
|
||||
)
|
||||
|
||||
// Test navigation with arrow left/right keys
|
||||
is(
|
||||
firstPageNavButton.view,
|
||||
mozPageNav.currentView,
|
||||
"The first page nav button is selected"
|
||||
)
|
||||
firstPageNavButton.buttonEl.focus();
|
||||
await arrowRight();
|
||||
ok(
|
||||
isActiveElement(secondPageNavButton),
|
||||
"The second page nav button is the active element after first arrow right"
|
||||
);
|
||||
is(
|
||||
secondPageNavButton.view,
|
||||
mozPageNav.currentView,
|
||||
"The second page nav button is selected"
|
||||
)
|
||||
await arrowRight();
|
||||
is(
|
||||
thirdPageNavButton.view,
|
||||
mozPageNav.currentView,
|
||||
"The third page nav button is selected"
|
||||
)
|
||||
await arrowRight();
|
||||
is(
|
||||
fourthPageNavButton.view,
|
||||
mozPageNav.currentView,
|
||||
"The fourth page nav button is selected"
|
||||
)
|
||||
await arrowRight();
|
||||
is(
|
||||
fifthPageNavButton.view,
|
||||
mozPageNav.currentView,
|
||||
"The fifth page nav button is selected"
|
||||
)
|
||||
await arrowRight();
|
||||
is(
|
||||
fifthPageNavButton.view,
|
||||
mozPageNav.currentView,
|
||||
"The fifth page nav button is still selected"
|
||||
)
|
||||
await arrowLeft();
|
||||
is(
|
||||
fourthPageNavButton.view,
|
||||
mozPageNav.currentView,
|
||||
"The fourth page nav button is selected"
|
||||
)
|
||||
await arrowLeft();
|
||||
is(
|
||||
thirdPageNavButton.view,
|
||||
mozPageNav.currentView,
|
||||
"The third page nav button is selected"
|
||||
)
|
||||
await arrowLeft();
|
||||
is(
|
||||
secondPageNavButton.view,
|
||||
mozPageNav.currentView,
|
||||
"The second page nav button is selected"
|
||||
)
|
||||
await arrowLeft();
|
||||
is(
|
||||
firstPageNavButton.view,
|
||||
mozPageNav.currentView,
|
||||
"The first page nav button is selected"
|
||||
)
|
||||
await arrowLeft();
|
||||
is(
|
||||
firstPageNavButton.view,
|
||||
mozPageNav.currentView,
|
||||
"The first page nav button is still selected"
|
||||
)
|
||||
|
||||
await SpecialPowers.popPrefEnv();
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -3,12 +3,15 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
:host {
|
||||
border-radius: 4px;
|
||||
border-radius: var(--border-radius-small);
|
||||
&:focus-visible {
|
||||
outline-offset: var(--page-nav-focus-outline-inset);
|
||||
}
|
||||
}
|
||||
|
||||
button {
|
||||
background-color: initial;
|
||||
border: 1px solid var(--in-content-primary-button-border-color);
|
||||
background-color: var(--page-nav-button-background-color);
|
||||
border: 1px solid var(--page-nav-border-color);
|
||||
-moz-context-properties: fill, fill-opacity;
|
||||
fill: currentColor;
|
||||
display: grid;
|
||||
|
|
@ -18,11 +21,11 @@ button {
|
|||
font-size: inherit;
|
||||
width: 100%;
|
||||
font-weight: normal;
|
||||
border-radius: 4px;
|
||||
color: inherit;
|
||||
border-radius: var(--page-nav-button-border-radius);
|
||||
color: var(--page-nav-button-text-color);
|
||||
text-align: start;
|
||||
transition: background-color 150ms;
|
||||
padding: var(--fxviewcategorynav-button-padding);
|
||||
padding: var(--page-nav-button-padding);
|
||||
}
|
||||
|
||||
button:hover {
|
||||
|
|
@ -38,8 +41,7 @@ button:hover {
|
|||
|
||||
button:hover,
|
||||
button[selected]:hover {
|
||||
background-color: var(--in-content-button-background-hover);
|
||||
border-color: var(--in-content-button-border-color-hover);
|
||||
background-color: var(--page-nav-button-background-color-hover);
|
||||
}
|
||||
|
||||
button[selected]:hover {
|
||||
|
|
@ -57,15 +59,15 @@ button:hover {
|
|||
}
|
||||
|
||||
button[selected]:not(:hover) {
|
||||
color: var(--in-content-accent-color);
|
||||
background-color: color-mix(in srgb, var(--fxview-primary-action-background) 5%, transparent);
|
||||
border-inline-start-color: var(--in-content-accent-color);
|
||||
color: var(--color-accent-primary);
|
||||
background-color: color-mix(in srgb, var(--page-nav-button-background-color-selected) 5%, transparent);
|
||||
border-inline-start-color: var(--color-accent-primary);
|
||||
}
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
button[selected] {
|
||||
background-color: color-mix(in srgb, var(--fxview-primary-action-background) 12%, transparent);
|
||||
background-color: color-mix(in srgb, var(--page-nav-button-background-color-selected) 12%, transparent);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -73,13 +75,10 @@ button:focus-visible,
|
|||
button[selected]:focus-visible {
|
||||
outline: var(--focus-outline);
|
||||
outline-offset: var(--focus-outline-offset);
|
||||
border-radius: var(--border-radius-small);
|
||||
}
|
||||
|
||||
.category-icon {
|
||||
background-color: initial;
|
||||
background-size: 20px;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
.page-nav-icon {
|
||||
height: 20px;
|
||||
width: 20px;
|
||||
-moz-context-properties: fill;
|
||||
|
|
@ -90,7 +89,7 @@ button[selected]:focus-visible {
|
|||
button {
|
||||
transition: none;
|
||||
border-color: ButtonText;
|
||||
background-color: var(--in-content-button-background);
|
||||
background-color: var(--button-background-color);
|
||||
}
|
||||
|
||||
button:hover {
|
||||
|
|
@ -105,8 +104,7 @@ button[selected]:focus-visible {
|
|||
}
|
||||
|
||||
slot {
|
||||
font-size: 1.13em;
|
||||
line-height: 1.4;
|
||||
font-size: var(--font-size-large);
|
||||
margin: 0;
|
||||
padding-inline-start: 0;
|
||||
user-select: none;
|
||||
76
toolkit/content/widgets/moz-page-nav/moz-page-nav.css
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
:host {
|
||||
--page-nav-button-border-radius: var(--button-border-radius);
|
||||
--page-nav-button-text-color: var(--button-text-color);
|
||||
--page-nav-button-background-color: transparent;
|
||||
--page-nav-button-background-color-hover: var(--button-background-color-hover);
|
||||
--page-nav-button-background-color-selected: var(--color-accent-primary);
|
||||
--page-nav-button-padding: var(--space-small);
|
||||
--page-nav-margin-top: 72px;
|
||||
--page-nav-margin-bottom: 36px;
|
||||
--page-nav-gap: 25px;
|
||||
--page-nav-button-gap: var(--space-xsmall);
|
||||
--page-nav-border-color: var(--border-color);
|
||||
--page-nav-focus-outline-inset: var(--focus-outline-inset);
|
||||
--page-nav-width: 240px;
|
||||
margin-inline-start: 42px;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
height: 100vh;
|
||||
width: var(--page-nav-width);
|
||||
|
||||
@media (prefers-reduced-motion) {
|
||||
/* (See Bug 1610081) Setting border-inline-end to add clear differentiation between side navigation and main content area */
|
||||
border-inline-end: 1px solid var(--page-nav-border-color);
|
||||
}
|
||||
|
||||
@media (max-width: 52rem) {
|
||||
grid-template-rows: 1fr auto;
|
||||
--page-nav-width: 118px;
|
||||
}
|
||||
}
|
||||
|
||||
nav {
|
||||
display: grid;
|
||||
grid-template-rows: min-content 1fr auto;
|
||||
gap: var(--page-nav-gap);
|
||||
margin-block: var(--page-nav-margin-top) var(--page-nav-margin-bottom);
|
||||
height: calc(100vh - var(--page-nav-margin-top) - var(--page-nav-margin-bottom));
|
||||
@media (max-width: 52rem) {
|
||||
grid-template-rows: 1fr auto;
|
||||
}
|
||||
}
|
||||
|
||||
.page-nav-header {
|
||||
/* Align the header text/icon with the page nav button icons */
|
||||
margin-inline-start: var(--page-nav-button-padding);
|
||||
font-size: var(--font-size-xlarge);
|
||||
font-weight: var(--font-weight-bold);
|
||||
margin-block: 0;
|
||||
|
||||
@media (max-width: 52rem) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.primary-nav-group,
|
||||
#secondary-nav-group {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr;
|
||||
grid-auto-rows: min-content;
|
||||
gap: var(--page-nav-button-gap);
|
||||
|
||||
@media (max-width: 52rem) {
|
||||
justify-content: center;
|
||||
grid-template-columns: min-content;
|
||||
}
|
||||
}
|
||||
|
||||
@media (prefers-contrast) {
|
||||
.primary-nav-group {
|
||||
gap: var(--space-small);
|
||||
}
|
||||
}
|
||||
170
toolkit/content/widgets/moz-page-nav/moz-page-nav.mjs
Normal file
|
|
@ -0,0 +1,170 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
import { html } from "chrome://global/content/vendor/lit.all.mjs";
|
||||
import { MozLitElement } from "chrome://global/content/lit-utils.mjs";
|
||||
|
||||
/**
|
||||
* A grouping of navigation buttons that is displayed at the page level,
|
||||
* intended to change the selected view, provide a heading, and have links
|
||||
* to external resources.
|
||||
*
|
||||
* @tagname moz-page-nav
|
||||
* @property {string} currentView - The currently selected view.
|
||||
* @property {string} heading - A heading to be displayed at the top of the navigation.
|
||||
* @slot [default] - Used to append moz-page-nav-button elements to the navigation.
|
||||
*/
|
||||
export default class MozPageNav extends MozLitElement {
|
||||
static properties = {
|
||||
currentView: { type: String },
|
||||
heading: { type: String },
|
||||
};
|
||||
|
||||
static queries = {
|
||||
headingEl: "#page-nav-header",
|
||||
primaryNavGroupSlot: ".primary-nav-group slot",
|
||||
secondaryNavGroupSlot: "#secondary-nav-group slot",
|
||||
};
|
||||
|
||||
get pageNavButtons() {
|
||||
return this.primaryNavGroupSlot
|
||||
.assignedNodes()
|
||||
.filter(
|
||||
node => node?.localName === "moz-page-nav-button" && !node.hidden
|
||||
);
|
||||
}
|
||||
|
||||
onChangeView(e) {
|
||||
this.currentView = e.target.view;
|
||||
}
|
||||
|
||||
handleFocus(e) {
|
||||
if (e.key == "ArrowDown" || e.key == "ArrowRight") {
|
||||
e.preventDefault();
|
||||
this.focusNextView();
|
||||
} else if (e.key == "ArrowUp" || e.key == "ArrowLeft") {
|
||||
e.preventDefault();
|
||||
this.focusPreviousView();
|
||||
}
|
||||
}
|
||||
|
||||
focusPreviousView() {
|
||||
let pageNavButtons = this.pageNavButtons;
|
||||
let currentIndex = pageNavButtons.findIndex(b => b.selected);
|
||||
let prev = pageNavButtons[currentIndex - 1];
|
||||
if (prev) {
|
||||
prev.activate();
|
||||
prev.buttonEl.focus();
|
||||
}
|
||||
}
|
||||
|
||||
focusNextView() {
|
||||
let pageNavButtons = this.pageNavButtons;
|
||||
let currentIndex = pageNavButtons.findIndex(b => b.selected);
|
||||
let next = pageNavButtons[currentIndex + 1];
|
||||
if (next) {
|
||||
next.activate();
|
||||
next.buttonEl.focus();
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
return html`
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="chrome://global/content/elements/moz-page-nav.css"
|
||||
/>
|
||||
<nav>
|
||||
<h1 class="page-nav-header" id="page-nav-header">${this.heading}</h1>
|
||||
<div
|
||||
class="primary-nav-group"
|
||||
role="tablist"
|
||||
aria-orientation="vertical"
|
||||
aria-labelledby="page-nav-header"
|
||||
>
|
||||
<slot
|
||||
@change-view=${this.onChangeView}
|
||||
@keydown=${this.handleFocus}
|
||||
></slot>
|
||||
</div>
|
||||
<div id="secondary-nav-group" role="group">
|
||||
<slot name="secondary-nav" @keydown=${this.handleFocus}></slot>
|
||||
</div>
|
||||
</nav>
|
||||
`;
|
||||
}
|
||||
|
||||
updated() {
|
||||
let isViewSelected = false;
|
||||
let assignedPageNavButtons = this.pageNavButtons;
|
||||
for (let button of assignedPageNavButtons) {
|
||||
button.selected = button.view == this.currentView;
|
||||
isViewSelected = isViewSelected || button.selected;
|
||||
}
|
||||
if (!isViewSelected && assignedPageNavButtons.length) {
|
||||
// Current page nav has no matching view, reset to the first view.
|
||||
assignedPageNavButtons[0].activate();
|
||||
}
|
||||
}
|
||||
}
|
||||
customElements.define("moz-page-nav", MozPageNav);
|
||||
|
||||
/**
|
||||
* A navigation button intended to change the selected view within a page.
|
||||
*
|
||||
* @tagname moz-page-nav-button
|
||||
* @property {string} iconSrc - The chrome:// url for the icon used for the button.
|
||||
* @property {string} l10nId - The fluent ID for the button's text
|
||||
* @property {boolean} selected - Whether or not the button is currently selected.
|
||||
* @slot [default] - Used to append the l10n string to the button.
|
||||
*/
|
||||
export class MozPageNavButton extends MozLitElement {
|
||||
static properties = {
|
||||
iconSrc: { type: String },
|
||||
l10nId: { type: String },
|
||||
selected: { type: Boolean },
|
||||
};
|
||||
|
||||
connectedCallback() {
|
||||
super.connectedCallback();
|
||||
this.setAttribute("role", "none");
|
||||
}
|
||||
|
||||
static queries = {
|
||||
buttonEl: "button",
|
||||
};
|
||||
|
||||
get view() {
|
||||
return this.getAttribute("view");
|
||||
}
|
||||
|
||||
activate() {
|
||||
this.dispatchEvent(
|
||||
new CustomEvent("change-view", {
|
||||
bubbles: true,
|
||||
composed: true,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
return html`
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="chrome://global/content/elements/moz-page-nav-button.css"
|
||||
/>
|
||||
<button
|
||||
aria-selected=${this.selected}
|
||||
tabindex=${this.selected ? 0 : -1}
|
||||
role="tab"
|
||||
?selected=${this.selected}
|
||||
@click=${this.activate}
|
||||
>
|
||||
<img class="page-nav-icon" src=${this.iconSrc} />
|
||||
<slot></slot>
|
||||
</button>
|
||||
`;
|
||||
}
|
||||
}
|
||||
customElements.define("moz-page-nav-button", MozPageNavButton);
|
||||
|
|
@ -0,0 +1,77 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
import { html } from "../vendor/lit.all.mjs";
|
||||
// eslint-disable-next-line import/no-unassigned-import
|
||||
import "./moz-page-nav.mjs";
|
||||
|
||||
export default {
|
||||
title: "UI Widgets/Page Nav",
|
||||
component: "moz-page-nav",
|
||||
parameters: {
|
||||
status: "in-development",
|
||||
actions: {
|
||||
handles: ["change-view"],
|
||||
},
|
||||
fluent: `
|
||||
moz-page-nav-button-one = View 1
|
||||
.title = View 1
|
||||
moz-page-nav-button-two = View 2
|
||||
.title = View 2
|
||||
moz-page-nav-button-three = View 3
|
||||
.title = View 3
|
||||
moz-page-link-one = Support Page
|
||||
.title = Support Page
|
||||
moz-page-nav-heading =
|
||||
.heading = Heading
|
||||
`,
|
||||
},
|
||||
};
|
||||
|
||||
const Template = () => html`
|
||||
<style>
|
||||
#page {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
|
||||
@media (max-width: 52rem) {
|
||||
grid-template-columns: 82px 1fr;
|
||||
}
|
||||
}
|
||||
moz-page-nav {
|
||||
margin-inline-start: 10px;
|
||||
--page-nav-margin-top: 10px;
|
||||
|
||||
@media (max-width: 52rem) {
|
||||
margin-inline-start: 0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<div id="page">
|
||||
<moz-page-nav data-l10n-id="moz-page-nav-heading" data-l10n-attrs="heading">
|
||||
<moz-page-nav-button
|
||||
view="view-one"
|
||||
data-l10n-id="moz-page-nav-button-one"
|
||||
iconSrc="chrome://browser/skin/preferences/category-general.svg"
|
||||
>
|
||||
</moz-page-nav-button>
|
||||
<moz-page-nav-button
|
||||
view="view-two"
|
||||
data-l10n-id="moz-page-nav-button-two"
|
||||
iconSrc="chrome://browser/skin/preferences/category-general.svg"
|
||||
>
|
||||
</moz-page-nav-button>
|
||||
<moz-page-nav-button
|
||||
view="view-three"
|
||||
data-l10n-id="moz-page-nav-button-three"
|
||||
iconSrc="chrome://browser/skin/preferences/category-general.svg"
|
||||
>
|
||||
</moz-page-nav-button>
|
||||
</moz-page-nav>
|
||||
<main></main>
|
||||
</div>
|
||||
`;
|
||||
|
||||
export const Default = Template.bind({});
|
||||
Default.args = {};
|
||||