Commit 3ebdac67 authored by John Lee's avatar John Lee Committed by Commit Bot

WebUI Tab Strip: Respond to tab grouped and ungrouped events

Bug: 1027373
Change-Id: Ie4acd431ce85b62fc0ff961e81e1f6a64dad5500
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2000969
Commit-Queue: John Lee <johntlee@chromium.org>
Reviewed-by: default avatarDemetrios Papadopoulos <dpapad@chromium.org>
Cr-Commit-Position: refs/heads/master@{#731844}
parent b0e22612
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
import {CustomElement} from './custom_element.js'; import {CustomElement} from './custom_element.js';
class TabGroupElement extends CustomElement { export class TabGroupElement extends CustomElement {
static get template() { static get template() {
return `{__html_template__}`; return `{__html_template__}`;
} }
......
...@@ -15,6 +15,7 @@ import {isRTL} from 'chrome://resources/js/util.m.js'; ...@@ -15,6 +15,7 @@ import {isRTL} from 'chrome://resources/js/util.m.js';
import {CustomElement} from './custom_element.js'; import {CustomElement} from './custom_element.js';
import {TabElement} from './tab.js'; import {TabElement} from './tab.js';
import {TabGroupElement} from './tab_group.js';
import {TabStripEmbedderProxy} from './tab_strip_embedder_proxy.js'; import {TabStripEmbedderProxy} from './tab_strip_embedder_proxy.js';
import {tabStripOptions} from './tab_strip_options.js'; import {tabStripOptions} from './tab_strip_options.js';
import {TabData, TabsApiProxy} from './tabs_api_proxy.js'; import {TabData, TabsApiProxy} from './tabs_api_proxy.js';
...@@ -53,6 +54,14 @@ function isTabElement(element) { ...@@ -53,6 +54,14 @@ function isTabElement(element) {
return element.tagName === 'TABSTRIP-TAB'; return element.tagName === 'TABSTRIP-TAB';
} }
/**
* @param {!Element} element
* @return {boolean}
*/
function isTabGroupElement(element) {
return element.tagName === 'TABSTRIP-TAB-GROUP';
}
class TabListElement extends CustomElement { class TabListElement extends CustomElement {
static get template() { static get template() {
return `{__html_template__}`; return `{__html_template__}`;
...@@ -265,6 +274,12 @@ class TabListElement extends CustomElement { ...@@ -265,6 +274,12 @@ class TabListElement extends CustomElement {
addWebUIListener('tab-updated', tab => this.onTabUpdated_(tab)); addWebUIListener('tab-updated', tab => this.onTabUpdated_(tab));
addWebUIListener( addWebUIListener(
'tab-active-changed', tabId => this.onTabActivated_(tabId)); 'tab-active-changed', tabId => this.onTabActivated_(tabId));
addWebUIListener(
'tab-group-state-changed',
(tabId, index, groupId) =>
this.onTabGroupStateChanged_(tabId, index, groupId));
addWebUIListener(
'tab-group-closed', groupId => this.onTabGroupClosed_(groupId));
}); });
} }
...@@ -299,6 +314,16 @@ class TabListElement extends CustomElement { ...@@ -299,6 +314,16 @@ class TabListElement extends CustomElement {
this.shadowRoot.querySelector(`tabstrip-tab[data-tab-id="${tabId}"]`)); this.shadowRoot.querySelector(`tabstrip-tab[data-tab-id="${tabId}"]`));
} }
/**
* @param {string} groupId
* @return {?TabGroupElement}
* @private
*/
findTabGroupElement_(groupId) {
return /** @type {?TabGroupElement} */ (this.shadowRoot.querySelector(
`tabstrip-tab-group[data-group-id="${groupId}"]`));
}
/** @private */ /** @private */
fetchAndUpdateColors_() { fetchAndUpdateColors_() {
this.tabStripEmbedderProxy_.getColors().then( this.tabStripEmbedderProxy_.getColors().then(
...@@ -324,24 +349,54 @@ class TabListElement extends CustomElement { ...@@ -324,24 +349,54 @@ class TabListElement extends CustomElement {
/** /**
* @param {!TabElement} tabElement * @param {!TabElement} tabElement
* @param {number} index * @param {number} modelIndex
* @private * @private
*/ */
insertTabOrMoveTo_(tabElement, index) { insertTabOrMoveTo_(tabElement, modelIndex) {
const isInserting = !tabElement.isConnected; const isInserting = !tabElement.isConnected;
// Remove the tabElement if it already exists in the DOM // Remove the tabElement if it already exists in the DOM
tabElement.remove(); tabElement.remove();
if (tabElement.tab && tabElement.tab.pinned) { if (tabElement.tab.pinned) {
this.pinnedTabsElement_.insertBefore( this.pinnedTabsElement_.insertBefore(
tabElement, this.pinnedTabsElement_.childNodes[index]); tabElement, this.pinnedTabsElement_.childNodes[modelIndex]);
} else { } else {
// Pinned tabs are in their own , so the index of non-pinned let elementToInsert = tabElement;
// tabs need to be offset by the number of pinned tabs let elementAtIndex =
const offsetIndex = index - this.pinnedTabsElement_.childElementCount; this.shadowRoot.querySelectorAll('tabstrip-tab').item(modelIndex);
this.unpinnedTabsElement_.insertBefore( let parentElement = this.unpinnedTabsElement_;
tabElement, this.unpinnedTabsElement_.childNodes[offsetIndex]);
if (tabElement.tab.groupId) {
let tabGroupElement = this.findTabGroupElement_(tabElement.tab.groupId);
if (tabGroupElement) {
// If a TabGroupElement already exists, add the TabElement to it.
parentElement = tabGroupElement;
} else {
// If a TabGroupElement does not exist, create one and add the
// TabGroupElement into the DOM.
tabGroupElement = document.createElement('tabstrip-tab-group');
tabGroupElement.setAttribute('data-group-id', tabElement.tab.groupId);
tabGroupElement.appendChild(tabElement);
elementToInsert = tabGroupElement;
}
} else if (
elementAtIndex && elementAtIndex.parentElement &&
isTabGroupElement(elementAtIndex.parentElement) &&
elementAtIndex.previousElementSibling === null) {
// If the element at the model index is in a group and is the first
// element in its group, insert the new element before its
// TabGroupElement. If a TabElement is being sandwiched between two
// TabElements in a group, it can be assumed that the tab will
// eventually be inserted into the group as well.
elementAtIndex = elementAtIndex.parentElement;
}
if (elementAtIndex && elementAtIndex.parentElement === parentElement) {
parentElement.insertBefore(elementToInsert, elementAtIndex);
} else {
parentElement.appendChild(elementToInsert);
}
} }
if (isInserting) { if (isInserting) {
...@@ -365,9 +420,16 @@ class TabListElement extends CustomElement { ...@@ -365,9 +420,16 @@ class TabListElement extends CustomElement {
this.scrollToActiveTab_(); this.scrollToActiveTab_();
} }
this.unpinnedTabsElement_.childNodes.forEach( this.unpinnedTabsElement_.childNodes.forEach(element => {
tabElement => this.updateThumbnailTrackStatus_( if (isTabGroupElement(/** @type {!Element} */ (element))) {
/** @type {!TabElement} */ (tabElement))); element.childNodes.forEach(
tabElement => this.updateThumbnailTrackStatus_(
/** @type {!TabElement} */ (tabElement)));
} else {
this.updateThumbnailTrackStatus_(
/** @type {!TabElement} */ (element));
}
});
} }
/** /**
...@@ -400,12 +462,9 @@ class TabListElement extends CustomElement { ...@@ -400,12 +462,9 @@ class TabListElement extends CustomElement {
event.dataTransfer.dropEffect = 'move'; event.dataTransfer.dropEffect = 'move';
let dragOverIndex = const dragOverIndex =
Array.from(dragOverItem.parentNode.children).indexOf(dragOverItem); Array.from(this.shadowRoot.querySelectorAll('tabstrip-tab'))
if (!this.draggedItem_.tab.pinned) { .indexOf(dragOverItem);
dragOverIndex += this.pinnedTabsElement_.childElementCount;
}
this.tabsApi_.moveTab(this.draggedItem_.tab.id, dragOverIndex); this.tabsApi_.moveTab(this.draggedItem_.tab.id, dragOverIndex);
} }
...@@ -498,6 +557,31 @@ class TabListElement extends CustomElement { ...@@ -498,6 +557,31 @@ class TabListElement extends CustomElement {
} }
} }
/**
* @param {string} groupId
* @private
*/
onTabGroupClosed_(groupId) {
const tabGroupElement = this.findTabGroupElement_(groupId);
if (!tabGroupElement) {
return;
}
tabGroupElement.remove();
}
/**
* @param {number} tabId
* @param {number} index
* @param {string} groupId
* @private
*/
onTabGroupStateChanged_(tabId, index, groupId) {
const tabElement = this.findTabElement_(tabId);
tabElement.tab = /** @type {!TabData} */ (
Object.assign({}, tabElement.tab, {groupId: groupId}));
this.insertTabOrMoveTo_(tabElement, index);
}
/** /**
* @param {number} tabId * @param {number} tabId
* @param {number} newIndex * @param {number} newIndex
......
...@@ -102,6 +102,10 @@ suite('TabList', () => { ...@@ -102,6 +102,10 @@ suite('TabList', () => {
return tabList.shadowRoot.querySelectorAll('#pinnedTabs tabstrip-tab'); return tabList.shadowRoot.querySelectorAll('#pinnedTabs tabstrip-tab');
} }
function getTabGroups() {
return tabList.shadowRoot.querySelectorAll('tabstrip-tab-group');
}
setup(() => { setup(() => {
document.body.innerHTML = ''; document.body.innerHTML = '';
document.body.style.margin = 0; document.body.style.margin = 0;
...@@ -219,6 +223,93 @@ suite('TabList', () => { ...@@ -219,6 +223,93 @@ suite('TabList', () => {
assertEquals(tabElements[0].tab, prependedTab); assertEquals(tabElements[0].tab, prependedTab);
}); });
test('AddNewTabGroup', () => {
const appendedTab = {
active: false,
alertStates: [],
groupId: 'group0',
id: 3,
index: 3,
title: 'New tab in group',
};
webUIListenerCallback('tab-created', appendedTab);
let tabElements = getUnpinnedTabs();
assertEquals(tabs.length + 1, tabElements.length);
assertEquals(getTabGroups().length, 1);
assertEquals(
'TABSTRIP-TAB-GROUP',
tabElements[appendedTab.index].parentElement.tagName);
const prependedTab = {
active: false,
alertStates: [],
groupId: 'group1',
id: 4,
index: 0,
title: 'New tab',
};
webUIListenerCallback('tab-created', prependedTab);
tabElements = getUnpinnedTabs();
assertEquals(tabs.length + 2, tabElements.length);
assertEquals(getTabGroups().length, 2);
assertEquals(
'TABSTRIP-TAB-GROUP',
tabElements[prependedTab.index].parentElement.tagName);
});
test('AddTabToExistingGroup', () => {
const appendedTab = {
active: false,
alertStates: [],
groupId: 'group0',
id: 3,
index: 3,
title: 'New tab in group',
};
webUIListenerCallback('tab-created', appendedTab);
const appendedTabInSameGroup = {
active: false,
alertStates: [],
groupId: 'group0',
id: 4,
index: 4,
title: 'New tab in same group',
};
webUIListenerCallback('tab-created', appendedTabInSameGroup);
const tabGroups = getTabGroups();
assertEquals(tabGroups.length, 1);
assertEquals(tabGroups[0].children.item(0).tab.id, appendedTab.id);
assertEquals(
tabGroups[0].children.item(1).tab.id, appendedTabInSameGroup.id);
});
// Test that the TabList does not add a non-grouped tab to a tab group at the
// same index.
test('HandleSingleTabNextToGroup', () => {
const tabInGroup = {
active: false,
alertStates: [],
groupId: 'group0',
id: 3,
index: 3,
title: 'New tab in group',
};
webUIListenerCallback('tab-created', tabInGroup);
const tabNotInGroup = {
active: false,
alertStates: [],
id: 4,
index: 3,
title: 'New tab not in group',
};
webUIListenerCallback('tab-created', tabNotInGroup);
const tabsContainerChildren =
tabList.shadowRoot.querySelector('#unpinnedTabs').children;
assertEquals(tabsContainerChildren.item(3).tagName, 'TABSTRIP-TAB');
assertEquals(tabsContainerChildren.item(3).tab, tabNotInGroup);
assertEquals(tabsContainerChildren.item(4).tagName, 'TABSTRIP-TAB-GROUP');
});
test('removes a tab when tab is removed from current window', async () => { test('removes a tab when tab is removed from current window', async () => {
const tabToRemove = tabs[0]; const tabToRemove = tabs[0];
webUIListenerCallback('tab-removed', tabToRemove.id); webUIListenerCallback('tab-removed', tabToRemove.id);
...@@ -293,6 +384,27 @@ suite('TabList', () => { ...@@ -293,6 +384,27 @@ suite('TabList', () => {
assertEquals(tabElementsBeforeMove[2], tabElementsAfterMove[1]); assertEquals(tabElementsBeforeMove[2], tabElementsAfterMove[1]);
}); });
test('MoveExistingTabToGroup', () => {
const tabToGroup = tabs[1];
webUIListenerCallback(
'tab-group-state-changed', tabToGroup.id, tabToGroup.index, 'group0');
let tabElements = getUnpinnedTabs();
assertEquals(tabElements.length, tabs.length);
assertEquals(
tabElements[tabToGroup.index].parentElement.tagName,
'TABSTRIP-TAB-GROUP');
const anotherTabToGroup = tabs[2];
webUIListenerCallback(
'tab-group-state-changed', anotherTabToGroup.id,
anotherTabToGroup.index, 'group0');
tabElements = getUnpinnedTabs();
assertEquals(tabElements.length, tabs.length);
assertEquals(
tabElements[tabToGroup.index].parentElement,
tabElements[anotherTabToGroup.index].parentElement);
});
test('dragstart sets a drag image offset by the event coordinates', () => { test('dragstart sets a drag image offset by the event coordinates', () => {
// Drag and drop only works for pinned tabs // Drag and drop only works for pinned tabs
tabs.forEach(pinTabAt); tabs.forEach(pinTabAt);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment