Bug 1908433 - Add UI for editing an existing tab group. r=jswinarton,dao,fluent-reviewers,desktop-theme-reviewers,tabbrowser-reviewers

Differential Revision: https://phabricator.services.mozilla.com/D223876
This commit is contained in:
DJ 2024-10-09 14:49:18 +00:00
parent bce997cb7f
commit 1b77a1b222
5 changed files with 177 additions and 5 deletions

View File

@ -27,7 +27,8 @@
role="menu"
norolluponanchor="true">
<html:div class="panel-header">
<html:h1 data-l10n-id="tab-group-editor-title-create"></html:h1>
<html:h1 class="tab-group-create-mode-only" data-l10n-id="tab-group-editor-title-create"></html:h1>
<html:h1 class="tab-group-edit-mode-only" data-l10n-id="tab-group-editor-title-edit"></html:h1>
</html:div>
<toolbarseparator />
<html:div class="panel-body tab-group-editor-name">
@ -36,14 +37,26 @@
</html:div>
<html:div class="panel-body tab-group-editor-swatches">
</html:div>
<html:moz-button-group class="panel-body tab-group-editor-actions">
<html:moz-button-group class="panel-body tab-group-create-actions tab-group-create-mode-only">
<html:moz-button id="tab-group-editor-button-cancel" data-l10n-id="tab-group-editor-cancel"></html:moz-button>
<html:moz-button type="primary" id="tab-group-editor-button-create" data-l10n-id="tab-group-editor-done"></html:moz-button>
</html:moz-button-group>
<toolbarseparator class="tab-group-edit-mode-only" />
<html:div class="panel-body tab-group-edit-actions tab-group-edit-mode-only">
<toolbarbutton id="tabGroupEditor_addNewTabInGroup" class="subviewbutton" data-l10n-id="tab-group-editor-action-add-new"></toolbarbutton>
<toolbarbutton disabled="true" id="tabGroupEditor_moveGroupToNewWindow" class="subviewbutton" data-l10n-id="tab-group-editor-action-new-window"></toolbarbutton>
<toolbarbutton disabled="true" id="tabGroupEditor_saveAndCloseGroup" class="subviewbutton" data-l10n-id="tab-group-editor-action-save"></toolbarbutton>
<toolbarbutton id="tabGroupEditor_ungroupTabs" class="subviewbutton" data-l10n-id="tab-group-editor-action-ungroup"></toolbarbutton>
</html:div>
<toolbarseparator class="tab-group-edit-mode-only" />
<html:div class="tab-group-edit-mode-only panel-body tab-group-delete">
<toolbarbutton disabled="true" id="tabGroupEditor_deleteGroup" class="subviewbutton" data-l10n-id="tab-group-editor-action-delete"></toolbarbutton>
</html:div>
</panel>
`;
#activeGroup;
#createMode;
#cancelButton;
#createButton;
#nameField;
@ -93,6 +106,18 @@
}
});
document
.getElementById("tabGroupEditor_addNewTabInGroup")
.addEventListener("command", () => {
this.#handleNewTabInGroup();
});
document
.getElementById("tabGroupEditor_ungroupTabs")
.addEventListener("command", () => {
this.#handleUngroup();
});
this.addEventListener("change", this);
}
@ -130,6 +155,15 @@
return this.#activeGroup;
}
get createMode() {
return this.#createMode;
}
set createMode(mode) {
this.#panel.classList.toggle("tab-group-editor-mode-create", mode);
this.#createMode = mode;
}
set activeGroup(group = null) {
this.#activeGroup = group;
this.#nameField.value = group ? group.label : "";
@ -148,6 +182,15 @@
openCreateModal(group) {
this.activeGroup = group;
this.createMode = true;
this.#panel.openPopup(group.firstChild, {
position: "bottomleft topleft",
});
}
openEditModal(group) {
this.activeGroup = group;
this.createMode = false;
this.#panel.openPopup(group.firstChild, {
position: "bottomleft topleft",
});
@ -174,6 +217,21 @@
this.#panel.hidePopup();
this.activeGroup = null;
}
async #handleNewTabInGroup() {
let lastTab = this.activeGroup?.tabs.at(-1);
let onTabOpened = async aEvent => {
this.activeGroup?.addTabs([aEvent.target]);
this.#panel.hidePopup();
window.removeEventListener("TabOpen", onTabOpened);
};
window.addEventListener("TabOpen", onTabOpened);
gBrowser.addAdjacentNewTab(lastTab);
}
#handleUngroup() {
this.activeGroup?.ungroupTabs();
}
}
customElements.define("tabgroup-menu", MozTabbrowserTabGroupMenu);

View File

@ -75,6 +75,12 @@
}
});
this._tabsChangedObserver.observe(this, { childList: true });
this.#labelElement.addEventListener("contextmenu", e => {
e.preventDefault();
gBrowser.tabGroupMenu.openEditModal(this);
return false;
});
}
disconnectedCallback() {

View File

@ -947,13 +947,15 @@ add_task(async function test_tabsContainNoTabGroups() {
* Tests behavior of the group management panel.
*/
add_task(async function test_tabGroupCreatePanel() {
let tabgroupPanel = document.getElementById("tab-group-editor").panel;
let tabgroupEditor = document.getElementById("tab-group-editor");
let tabgroupPanel = tabgroupEditor.panel;
let nameField = tabgroupPanel.querySelector("#tab-group-name");
let tab = BrowserTestUtils.addTab(gBrowser, "about:blank");
let panelShown = BrowserTestUtils.waitForPopupEvent(tabgroupPanel, "shown");
let group = gBrowser.addTabGroup("cyan", "Food", [tab]);
await panelShown;
Assert.ok(tabgroupEditor.createMode, "Group editor is in create mode");
// Edit panel should be populated with correct group details
Assert.ok(
nameField.value == group.label,
@ -1002,5 +1004,82 @@ add_task(async function test_tabGroupCreatePanel() {
Assert.ok(group.label == "Shopping");
Assert.ok(group.color == "red");
await removeTabGroup(group);
// right-clicking on the group label reopens the panel in edit mode
panelShown = BrowserTestUtils.waitForPopupEvent(tabgroupPanel, "shown");
EventUtils.synthesizeMouseAtCenter(
group.querySelector(".tab-group-label"),
{ type: "contextmenu", button: 2 },
window
);
await panelShown;
Assert.ok(tabgroupPanel.state == "open", "Tabgroup edit panel is open");
Assert.ok(!tabgroupEditor.createMode, "Group editor is not in create mode");
panelHidden = BrowserTestUtils.waitForPopupEvent(tabgroupPanel, "hidden");
EventUtils.synthesizeKey("KEY_Escape");
await panelHidden;
gBrowser.removeTabGroup(group, { animate: false });
});
async function createTabGroupAndOpenEditPanel() {
let tabgroupEditor = document.getElementById("tab-group-editor");
let tabgroupPanel = tabgroupEditor.panel;
let tab = BrowserTestUtils.addTab(gBrowser, "about:blank", {
animate: false,
});
let panelShown = BrowserTestUtils.waitForPopupEvent(tabgroupPanel, "shown");
let group = gBrowser.addTabGroup("cyan", "Food", [tab]);
await panelShown;
// Panel dismissed after clicking Create and group remains
let panelHidden = BrowserTestUtils.waitForPopupEvent(tabgroupPanel, "hidden");
tabgroupPanel.querySelector("#tab-group-editor-button-create").click();
await panelHidden;
panelShown = BrowserTestUtils.waitForPopupEvent(tabgroupPanel, "shown");
EventUtils.synthesizeMouseAtCenter(
group.querySelector(".tab-group-label"),
{ type: "contextmenu", button: 2 },
window
);
return new Promise(resolve => {
panelShown.then(() => {
resolve({ tabgroupEditor, tabgroupPanel, tab, group });
});
});
}
add_task(async function test_tabGroupPanelAddTab() {
let { tabgroupPanel, group } = await createTabGroupAndOpenEditPanel();
let addNewTabButton = tabgroupPanel.querySelector(
"#tabGroupEditor_addNewTabInGroup"
);
Assert.equal(group.tabs.length, 1, "Group has 1 tab");
let panelHidden = BrowserTestUtils.waitForPopupEvent(tabgroupPanel, "hidden");
addNewTabButton.click();
await panelHidden;
Assert.ok(tabgroupPanel.state === "closed", "Group editor is closed");
Assert.equal(group.tabs.length, 2, "Group has 2 tabs");
for (let tab of group.tabs) {
BrowserTestUtils.removeTab(tab);
}
});
add_task(async function test_tabGroupPanelUngroupTabs() {
let { tabgroupPanel, tab, group } = await createTabGroupAndOpenEditPanel();
let ungroupTabsButton = tabgroupPanel.querySelector(
"#tabGroupEditor_ungroupTabs"
);
Assert.ok(tab.group.id == group.id, "Tab is in group");
let panelHidden = BrowserTestUtils.waitForPopupEvent(tabgroupPanel, "hidden");
ungroupTabsButton.click();
await panelHidden;
Assert.ok(!tab.group, "Tab is no longer grouped");
BrowserTestUtils.removeTab(tab);
});

View File

@ -23,6 +23,11 @@ tab-context-move-tab-to-group =
}
.accesskey = G
tab-group-editor-action-add-new = Add tab to group
tab-group-editor-action-new-window = Move group to new window
tab-group-editor-action-save = Save and close group
tab-group-editor-action-ungroup = Ungroup tabs
tab-group-editor-action-delete = Delete group
tab-group-editor-done =
.label = Done
.accessKey = D

View File

@ -788,16 +788,25 @@ tab-group {
.panel-header {
min-height: auto;
> h1 {
margin-top: 0;
margin-bottom: var(--space-small);
}
}
toolbarseparator {
margin-top: var(--space-medium);
margin-block: var(--space-medium);
}
.panel-body {
padding-block: var(--space-medium);
}
&.tab-group-editor-mode-create .tab-group-edit-mode-only,
&:not(.tab-group-editor-mode-create) .tab-group-create-mode-only {
display: none;
}
.tab-group-editor-name {
display: flex;
flex-direction: column;
@ -836,6 +845,21 @@ tab-group {
border-radius: 20%;
background-color: light-dark(var(--tabgroup-swatch-color), var(--tabgroup-swatch-color-invert));
}
.tab-group-edit-actions,
.tab-group-delete {
padding-block: 0;
> toolbarbutton {
justify-content: flex-start;
}
}
toolbarbutton {
margin: 0;
}
.tab-group-delete label {
color: var(--text-color-error);
}
}