Bug 1770852 - Re-implement placeholders and fix tests for the tree list view. r=darktrojan
Differential Revision: https://phabricator.services.mozilla.com/D163389 --HG-- extra : amend_source : 7254cbb44be671b8b3ffa32800e2435bd39d96cd
This commit is contained in:
Родитель
0d5f4bd539
Коммит
3401ee7dca
|
@ -21,6 +21,10 @@
|
|||
}
|
||||
this.hasConnected = true;
|
||||
|
||||
// Prevent this element from being part of the roving tab focus since we
|
||||
// handle that independently for the TreeViewListbox and we don't want any
|
||||
// interference from this.
|
||||
this.tabIndex = -1;
|
||||
this.classList.add("tree-view-scrollable-container");
|
||||
|
||||
this.table = document.createElement("table", { is: "tree-view-table" });
|
||||
|
@ -606,6 +610,9 @@
|
|||
this.setAttribute("aria-multiselectable", "true");
|
||||
|
||||
this.scrollable = this.closest(".tree-view-scrollable-container");
|
||||
this.placeholder = this.scrollable.querySelector(
|
||||
`slot[name="placeholders"]`
|
||||
);
|
||||
|
||||
this.addEventListener("focus", event => {
|
||||
if (this._preventFocusHandler) {
|
||||
|
@ -972,7 +979,10 @@
|
|||
* here is important.
|
||||
*/
|
||||
_ensureVisibleRowsAreDisplayed() {
|
||||
if (!this.view || this.view.rowCount == 0) {
|
||||
let hasRows = !this.view || this.view.rowCount == 0;
|
||||
this.placeholder?.classList.toggle("show", hasRows);
|
||||
|
||||
if (hasRows) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1064,7 +1074,12 @@
|
|||
return;
|
||||
}
|
||||
|
||||
const bottomIndex = topIndex + this._rowElementClass.ROW_HEIGHT * 3;
|
||||
// Account for the table header height in a sticky position above the
|
||||
// listbox. If the list is not in a table layout, the thead height is 0.
|
||||
const bottomIndex =
|
||||
topIndex +
|
||||
this._rowElementClass.ROW_HEIGHT +
|
||||
this.closest("table").header.clientHeight;
|
||||
if (bottomIndex > scrollTop + clientHeight) {
|
||||
this.scrollable.scrollTo(0, bottomIndex - clientHeight);
|
||||
}
|
||||
|
@ -1367,6 +1382,17 @@
|
|||
|
||||
return selected;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loop through all available child elements of the placeholder slot and
|
||||
* show those that are needed.
|
||||
* @param {array} idsToShow - Array of ids to show.
|
||||
*/
|
||||
updatePlaceholders(idsToShow) {
|
||||
for (let element of this.placeholder.children) {
|
||||
element.hidden = !idsToShow.includes(element.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
customElements.define("tree-view-listbox", TreeViewListbox, {
|
||||
extends: "tbody",
|
||||
|
|
|
@ -27,12 +27,12 @@ add_task(async function testKeyboardAndMouse() {
|
|||
async function subtestKeyboardAndMouse() {
|
||||
let doc = content.document;
|
||||
|
||||
let list = doc.querySelector("tree-view-listbox");
|
||||
let list = doc.querySelector(`[is="tree-view-listbox"]`);
|
||||
Assert.ok(!!list, "the list exists");
|
||||
|
||||
let listRect = list.getBoundingClientRect();
|
||||
let listRect = list.scrollable.getBoundingClientRect();
|
||||
|
||||
let rows = list.querySelectorAll("test-listrow");
|
||||
let rows = list.querySelectorAll(`tr[is="test-listrow"]`);
|
||||
// Count is calculated from the height of `list` divided by
|
||||
// TestCardRow.ROW_HEIGHT, plus TreeViewListbox.OVERFLOW_BUFFER.
|
||||
Assert.equal(rows.length, 23, "the list has the right number of rows");
|
||||
|
@ -85,6 +85,23 @@ async function subtestKeyboardAndMouse() {
|
|||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if the spacerTop TBODY of the TreeViewTable is properly allocating
|
||||
* the height of non existing rows.
|
||||
*
|
||||
* @param {int} rows - The number of rows that the spacerTop should be
|
||||
* simulating their height allocation.
|
||||
*/
|
||||
function checkTopSpacerHeight(rows) {
|
||||
let table = doc.querySelector(`[is="tree-view-table"]`);
|
||||
// -10 to account for the OVERFLOW_BUFFER.
|
||||
Assert.equal(
|
||||
table.spacerTop.clientHeight,
|
||||
list.getRowAtIndex(rows).clientHeight * (rows - 10),
|
||||
"The top spacer has the correct height"
|
||||
);
|
||||
}
|
||||
|
||||
function checkCurrent(expectedIndex) {
|
||||
Assert.equal(list.currentIndex, expectedIndex, "currentIndex is correct");
|
||||
if (selectHandler.currentAtEvent !== null) {
|
||||
|
@ -357,6 +374,7 @@ async function subtestKeyboardAndMouse() {
|
|||
38,
|
||||
"scrolled to the correct place"
|
||||
);
|
||||
checkTopSpacerHeight(37);
|
||||
|
||||
// Does nothing.
|
||||
await pressKey("VK_DOWN", {}, false);
|
||||
|
@ -367,6 +385,7 @@ async function subtestKeyboardAndMouse() {
|
|||
38,
|
||||
"scrolled to the correct place"
|
||||
);
|
||||
checkTopSpacerHeight(37);
|
||||
|
||||
await pressKey("VK_PAGE_UP");
|
||||
await scrollingDelay();
|
||||
|
@ -387,6 +406,7 @@ async function subtestKeyboardAndMouse() {
|
|||
38,
|
||||
"scrolled to the correct place"
|
||||
);
|
||||
checkTopSpacerHeight(37);
|
||||
|
||||
await pressKey("VK_HOME");
|
||||
await scrollingDelay();
|
||||
|
@ -398,7 +418,7 @@ async function subtestKeyboardAndMouse() {
|
|||
// even if the row element itself disappears.
|
||||
|
||||
selectHandler.reset();
|
||||
list.scrollTo(0, 125);
|
||||
list.scrollable.scrollTo(0, 125);
|
||||
await scrollingDelay();
|
||||
checkCurrent(0);
|
||||
checkSelected(0);
|
||||
|
@ -408,7 +428,7 @@ async function subtestKeyboardAndMouse() {
|
|||
"getFirstVisibleIndex is correct"
|
||||
);
|
||||
|
||||
list.scrollTo(0, 1025);
|
||||
list.scrollable.scrollTo(0, 1025);
|
||||
await scrollingDelay();
|
||||
Assert.equal(list.currentIndex, 0, "currentIndex is still set");
|
||||
Assert.ok(
|
||||
|
@ -429,6 +449,7 @@ async function subtestKeyboardAndMouse() {
|
|||
!selectHandler.seenEvent,
|
||||
"'select' event did not fire as expected"
|
||||
);
|
||||
checkTopSpacerHeight(20);
|
||||
|
||||
await pressKey("VK_DOWN");
|
||||
await scrollingDelay();
|
||||
|
@ -437,7 +458,7 @@ async function subtestKeyboardAndMouse() {
|
|||
Assert.equal(list.getFirstVisibleIndex(), 1, "scrolled to the correct place");
|
||||
|
||||
selectHandler.reset();
|
||||
list.scrollTo(0, 0);
|
||||
list.scrollable.scrollTo(0, 0);
|
||||
await scrollingDelay();
|
||||
checkCurrent(1);
|
||||
checkSelected(1);
|
||||
|
@ -459,30 +480,58 @@ async function subtestKeyboardAndMouse() {
|
|||
// Some literal edge cases. Clicking on a partially visible row should
|
||||
// scroll it into view.
|
||||
|
||||
rows = list.querySelectorAll("test-listrow");
|
||||
rows = list.querySelectorAll(`tr[is="test-listrow"]`);
|
||||
let bcr = rows[12].getBoundingClientRect();
|
||||
Assert.less(bcr.top, listRect.bottom, "top of row 12 is visible");
|
||||
Assert.less(
|
||||
Math.round(bcr.top),
|
||||
Math.round(listRect.bottom),
|
||||
"top of row 12 is visible"
|
||||
);
|
||||
Assert.greater(
|
||||
bcr.bottom,
|
||||
listRect.bottom,
|
||||
Math.round(bcr.bottom),
|
||||
Math.round(listRect.bottom),
|
||||
"bottom of row 12 is not visible"
|
||||
);
|
||||
await clickOnRow(12);
|
||||
await scrollingDelay();
|
||||
rows = list.querySelectorAll("test-listrow");
|
||||
rows = list.querySelectorAll(`tr[is="test-listrow"]`);
|
||||
bcr = rows[12].getBoundingClientRect();
|
||||
Assert.less(bcr.top, listRect.bottom, "top of row 12 is visible");
|
||||
Assert.equal(bcr.bottom, listRect.bottom, "bottom of row 12 is visible");
|
||||
Assert.less(
|
||||
Math.round(bcr.top),
|
||||
Math.round(listRect.bottom),
|
||||
"top of row 12 is visible"
|
||||
);
|
||||
Assert.equal(
|
||||
Math.round(bcr.bottom),
|
||||
Math.round(listRect.bottom),
|
||||
"bottom of row 12 is visible"
|
||||
);
|
||||
|
||||
bcr = rows[0].getBoundingClientRect();
|
||||
Assert.less(bcr.top, listRect.top, "top of row 0 is not visible");
|
||||
Assert.greater(bcr.bottom, listRect.top, "bottom of row 0 is visible");
|
||||
Assert.less(
|
||||
Math.round(bcr.top),
|
||||
Math.round(listRect.top),
|
||||
"top of row 0 is not visible"
|
||||
);
|
||||
Assert.greater(
|
||||
Math.round(bcr.bottom),
|
||||
Math.round(listRect.top),
|
||||
"bottom of row 0 is visible"
|
||||
);
|
||||
await clickOnRow(0);
|
||||
await scrollingDelay();
|
||||
rows = list.querySelectorAll("test-listrow");
|
||||
rows = list.querySelectorAll(`tr[is="test-listrow"]`);
|
||||
bcr = rows[0].getBoundingClientRect();
|
||||
Assert.equal(bcr.top, listRect.top, "top of row 0 is visible");
|
||||
Assert.greater(bcr.bottom, listRect.top, "bottom of row 0 is visible");
|
||||
Assert.equal(
|
||||
Math.round(bcr.top),
|
||||
Math.round(listRect.top),
|
||||
"top of row 0 is visible"
|
||||
);
|
||||
Assert.greater(
|
||||
Math.round(bcr.bottom),
|
||||
Math.round(listRect.top),
|
||||
"bottom of row 0 is visible"
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -506,7 +555,7 @@ async function subtestRowCountChange() {
|
|||
let doc = content.document;
|
||||
|
||||
let ROW_HEIGHT = 50;
|
||||
let list = doc.querySelector("tree-view-listbox");
|
||||
let list = doc.querySelector(`[is="tree-view-listbox"]`);
|
||||
let view = list.view;
|
||||
let rows;
|
||||
|
||||
|
@ -517,7 +566,7 @@ async function subtestRowCountChange() {
|
|||
for (let i = first; i <= last; i++) {
|
||||
expectedIndices.push(i);
|
||||
}
|
||||
rows = list.querySelectorAll("test-listrow");
|
||||
rows = list.querySelectorAll(`tr[is="test-listrow"]`);
|
||||
Assert.deepEqual(
|
||||
Array.from(rows, r => r.index),
|
||||
expectedIndices,
|
||||
|
@ -532,7 +581,7 @@ async function subtestRowCountChange() {
|
|||
|
||||
function checkSelected(indices, existingIndices) {
|
||||
Assert.deepEqual(list.selectedIndices, indices);
|
||||
let selectedRows = list.querySelectorAll("test-listrow.selected");
|
||||
let selectedRows = list.querySelectorAll(`tr[is="test-listrow"].selected`);
|
||||
Assert.deepEqual(
|
||||
Array.from(selectedRows, r => r.index),
|
||||
existingIndices
|
||||
|
@ -559,7 +608,7 @@ async function subtestRowCountChange() {
|
|||
|
||||
list.rowCountChanged(index, values.length);
|
||||
Assert.equal(
|
||||
list.scrollHeight,
|
||||
list.scrollable.scrollHeight,
|
||||
expectedCount * ROW_HEIGHT,
|
||||
"space for all rows is allocated"
|
||||
);
|
||||
|
@ -581,7 +630,7 @@ async function subtestRowCountChange() {
|
|||
|
||||
list.rowCountChanged(index, -count);
|
||||
Assert.equal(
|
||||
list.scrollHeight,
|
||||
list.scrollable.scrollHeight,
|
||||
expectedCount * ROW_HEIGHT,
|
||||
"space for all rows is allocated"
|
||||
);
|
||||
|
@ -592,9 +641,9 @@ async function subtestRowCountChange() {
|
|||
expectedCount,
|
||||
"the view has the right number of rows"
|
||||
);
|
||||
Assert.equal(list.scrollTop, 0, "the list is scrolled to the top");
|
||||
Assert.equal(list.scrollable.scrollTop, 0, "the list is scrolled to the top");
|
||||
Assert.equal(
|
||||
list.scrollHeight,
|
||||
list.scrollable.scrollHeight,
|
||||
expectedCount * ROW_HEIGHT,
|
||||
"space for all rows is allocated"
|
||||
);
|
||||
|
@ -664,7 +713,11 @@ async function subtestRowCountChange() {
|
|||
Assert.equal(rows[22].dataset.value, "17");
|
||||
checkSelected([7, 19, 32, 42, 54], [7, 19]);
|
||||
|
||||
Assert.equal(list.scrollTop, 0, "the list is still scrolled to the top");
|
||||
Assert.equal(
|
||||
list.scrollable.scrollTop,
|
||||
0,
|
||||
"the list is still scrolled to the top"
|
||||
);
|
||||
|
||||
// Remove values in the order we added them.
|
||||
|
||||
|
@ -715,11 +768,15 @@ async function subtestRowCountChange() {
|
|||
Assert.equal(rows[22].dataset.value, "22");
|
||||
checkSelected([4, 14, 24, 34, 44], [4, 14]);
|
||||
|
||||
Assert.equal(list.scrollTop, 0, "the list is still scrolled to the top");
|
||||
Assert.equal(
|
||||
list.scrollable.scrollTop,
|
||||
0,
|
||||
"the list is still scrolled to the top"
|
||||
);
|
||||
|
||||
// Now scroll to the middle and repeat.
|
||||
|
||||
list.scrollTo(0, 935);
|
||||
list.scrollable.scrollTo(0, 935);
|
||||
await new Promise(r => content.setTimeout(r, 100));
|
||||
checkRows(8, 41);
|
||||
Assert.equal(rows[0].dataset.value, "8");
|
||||
|
@ -765,7 +822,11 @@ async function subtestRowCountChange() {
|
|||
Assert.equal(rows[33].dataset.value, "37a");
|
||||
checkSelected([5, 16, 26, 37, 48], [16, 26, 37]);
|
||||
|
||||
Assert.equal(list.scrollTop, 935, "the list is still scrolled to the middle");
|
||||
Assert.equal(
|
||||
list.scrollable.scrollTop,
|
||||
935,
|
||||
"the list is still scrolled to the middle"
|
||||
);
|
||||
|
||||
removeValues(54, 1, [50]);
|
||||
checkRows(8, 41);
|
||||
|
@ -797,11 +858,15 @@ async function subtestRowCountChange() {
|
|||
Assert.equal(rows[33].dataset.value, "41");
|
||||
checkSelected([4, 14, 24, 34, 44], [14, 24, 34]);
|
||||
|
||||
Assert.equal(list.scrollTop, 935, "the list is still scrolled to the middle");
|
||||
Assert.equal(
|
||||
list.scrollable.scrollTop,
|
||||
935,
|
||||
"the list is still scrolled to the middle"
|
||||
);
|
||||
|
||||
// Now scroll to the bottom and repeat.
|
||||
|
||||
list.scrollTo(0, 1870);
|
||||
list.scrollable.scrollTo(0, 1870);
|
||||
await new Promise(r => content.setTimeout(r, 100));
|
||||
checkRows(27, 49);
|
||||
Assert.equal(rows[0].dataset.value, "27");
|
||||
|
@ -840,7 +905,7 @@ async function subtestRowCountChange() {
|
|||
checkSelected([5, 15, 25, 36, 46], [36, 46]);
|
||||
|
||||
Assert.equal(
|
||||
list.scrollTop,
|
||||
list.scrollable.scrollTop,
|
||||
1870,
|
||||
"the list is still scrolled to the bottom"
|
||||
);
|
||||
|
@ -873,14 +938,14 @@ async function subtestRowCountChange() {
|
|||
checkSelected([4, 14, 24, 34, 44], [34, 44]);
|
||||
|
||||
Assert.equal(
|
||||
list.scrollTop,
|
||||
list.scrollable.scrollTop,
|
||||
1870,
|
||||
"the list is still scrolled to the bottom"
|
||||
);
|
||||
|
||||
// Remove a selected row and check the selection changes.
|
||||
|
||||
list.scrollTo(0, 0);
|
||||
list.scrollable.scrollTo(0, 0);
|
||||
await new Promise(r => content.setTimeout(r, 100));
|
||||
|
||||
checkSelected([4, 14, 24, 34, 44], [4, 14]);
|
||||
|
@ -935,7 +1000,7 @@ add_task(async function testExpandCollapse() {
|
|||
|
||||
async function subtestExpandCollapse() {
|
||||
let doc = content.document;
|
||||
let list = doc.querySelector("tree-view-listbox");
|
||||
let list = doc.querySelector(`[is="tree-view-listbox"]`);
|
||||
let allIds = [
|
||||
"row-1",
|
||||
"row-2",
|
||||
|
@ -1461,12 +1526,12 @@ add_task(async function testRowClassChange() {
|
|||
|
||||
async function subtestRowClassChange() {
|
||||
let doc = content.document;
|
||||
let list = doc.querySelector("tree-view-listbox");
|
||||
let list = doc.querySelector(`[is="tree-view-listbox"]`);
|
||||
let indices = (list.selectedIndices = [1, 2, 3, 5, 8, 13, 21, 34]);
|
||||
list.currentIndex = 5;
|
||||
|
||||
for (let row of list.children) {
|
||||
Assert.equal(row.localName, "test-listrow");
|
||||
Assert.equal(row.getAttribute("is"), "test-listrow");
|
||||
Assert.equal(row.clientHeight, 50);
|
||||
Assert.equal(
|
||||
row.classList.contains("selected"),
|
||||
|
@ -1481,7 +1546,7 @@ async function subtestRowClassChange() {
|
|||
Assert.equal(list.currentIndex, 5);
|
||||
|
||||
for (let row of list.children) {
|
||||
Assert.equal(row.localName, "alternative-listrow");
|
||||
Assert.equal(row.getAttribute("is"), "alternative-listrow");
|
||||
Assert.equal(row.clientHeight, 80);
|
||||
Assert.equal(
|
||||
row.classList.contains("selected"),
|
||||
|
@ -1500,7 +1565,7 @@ async function subtestRowClassChange() {
|
|||
Assert.equal(list.currentIndex, -1);
|
||||
|
||||
for (let row of list.children) {
|
||||
Assert.equal(row.localName, "test-listrow");
|
||||
Assert.equal(row.getAttribute("is"), "test-listrow");
|
||||
Assert.equal(row.clientHeight, 50);
|
||||
Assert.ok(!row.classList.contains("selected"));
|
||||
Assert.ok(!row.classList.contains("current"));
|
||||
|
|
|
@ -1,96 +1,118 @@
|
|||
class TestCardRow extends customElements.get("tree-view-listrow") {
|
||||
static ROW_HEIGHT = 50;
|
||||
|
||||
connectedCallback() {
|
||||
if (this.hasConnected) {
|
||||
return;
|
||||
}
|
||||
|
||||
super.connectedCallback();
|
||||
|
||||
this.d1 = this.appendChild(document.createElement("div"));
|
||||
this.d1.classList.add("d1");
|
||||
|
||||
this.d2 = this.d1.appendChild(document.createElement("div"));
|
||||
this.d2.classList.add("d2");
|
||||
|
||||
this.d3 = this.d1.appendChild(document.createElement("div"));
|
||||
this.d3.classList.add("d3");
|
||||
}
|
||||
|
||||
get index() {
|
||||
return super.index;
|
||||
}
|
||||
|
||||
set index(index) {
|
||||
super.index = index;
|
||||
this.d2.textContent = this.view.getCellText(index, {
|
||||
id: "GeneratedName",
|
||||
});
|
||||
this.d3.textContent = this.view.getCellText(index, {
|
||||
id: "PrimaryEmail",
|
||||
});
|
||||
this.dataset.value = this.view.values[index];
|
||||
}
|
||||
}
|
||||
customElements.define("test-listrow", TestCardRow);
|
||||
|
||||
class AlternativeCardRow extends customElements.get("tree-view-listrow") {
|
||||
static ROW_HEIGHT = 80;
|
||||
|
||||
connectedCallback() {
|
||||
if (this.hasConnected) {
|
||||
return;
|
||||
}
|
||||
|
||||
super.connectedCallback();
|
||||
}
|
||||
|
||||
get index() {
|
||||
return super.index;
|
||||
}
|
||||
|
||||
set index(index) {
|
||||
super.index = index;
|
||||
this.textContent = this.view.getCellText(index, { id: "GeneratedName" });
|
||||
}
|
||||
}
|
||||
customElements.define("alternative-listrow", AlternativeCardRow);
|
||||
|
||||
class TestView {
|
||||
values = [];
|
||||
|
||||
constructor(start, count) {
|
||||
for (let i = start; i < start + count; i++) {
|
||||
this.values.push(i);
|
||||
}
|
||||
}
|
||||
|
||||
get rowCount() {
|
||||
return this.values.length;
|
||||
}
|
||||
|
||||
getCellText(index, column) {
|
||||
return `${column.id} ${this.values[index]}`;
|
||||
}
|
||||
|
||||
isContainer() {
|
||||
return false;
|
||||
}
|
||||
|
||||
isContainerOpen() {
|
||||
return false;
|
||||
}
|
||||
|
||||
selectionChanged() {}
|
||||
|
||||
setTree() {}
|
||||
}
|
||||
/* 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/. */
|
||||
|
||||
// FIXME: Wrap the whole method around the document load listener to prevent the
|
||||
// undefined state of the "tree-view-listrow" element. This is due to the .mjs
|
||||
// nature of the class file.
|
||||
window.addEventListener("load", () => {
|
||||
let list = document.getElementById("testList");
|
||||
list.addEventListener("select", event => {
|
||||
console.log("select event, selected indices:", list.selectedIndices);
|
||||
class TestCardRow extends customElements.get("tree-view-listrow") {
|
||||
static ROW_HEIGHT = 50;
|
||||
|
||||
connectedCallback() {
|
||||
if (this.hasConnected) {
|
||||
return;
|
||||
}
|
||||
|
||||
super.connectedCallback();
|
||||
|
||||
this.cell = this.appendChild(document.createElement("td"));
|
||||
let container = this.cell.appendChild(document.createElement("div"));
|
||||
|
||||
this.d1 = container.appendChild(document.createElement("div"));
|
||||
this.d1.classList.add("d1");
|
||||
|
||||
this.d2 = this.d1.appendChild(document.createElement("div"));
|
||||
this.d2.classList.add("d2");
|
||||
|
||||
this.d3 = this.d1.appendChild(document.createElement("div"));
|
||||
this.d3.classList.add("d3");
|
||||
}
|
||||
|
||||
get index() {
|
||||
return super.index;
|
||||
}
|
||||
|
||||
set index(index) {
|
||||
super.index = index;
|
||||
this.d2.textContent = this.view.getCellText(index, {
|
||||
id: "GeneratedName",
|
||||
});
|
||||
this.d3.textContent = this.view.getCellText(index, {
|
||||
id: "PrimaryEmail",
|
||||
});
|
||||
this.dataset.value = this.view.values[index];
|
||||
}
|
||||
}
|
||||
customElements.define("test-listrow", TestCardRow, { extends: "tr" });
|
||||
|
||||
class AlternativeCardRow extends customElements.get("tree-view-listrow") {
|
||||
static ROW_HEIGHT = 80;
|
||||
|
||||
connectedCallback() {
|
||||
if (this.hasConnected) {
|
||||
return;
|
||||
}
|
||||
|
||||
super.connectedCallback();
|
||||
|
||||
this.cell = this.appendChild(document.createElement("td"));
|
||||
}
|
||||
|
||||
get index() {
|
||||
return super.index;
|
||||
}
|
||||
|
||||
set index(index) {
|
||||
super.index = index;
|
||||
this.cell.textContent = this.view.getCellText(index, {
|
||||
id: "GeneratedName",
|
||||
});
|
||||
}
|
||||
}
|
||||
customElements.define("alternative-listrow", AlternativeCardRow, {
|
||||
extends: "tr",
|
||||
});
|
||||
list.view = new TestView(0, 50);
|
||||
|
||||
class TestView {
|
||||
values = [];
|
||||
|
||||
constructor(start, count) {
|
||||
for (let i = start; i < start + count; i++) {
|
||||
this.values.push(i);
|
||||
}
|
||||
}
|
||||
|
||||
get rowCount() {
|
||||
return this.values.length;
|
||||
}
|
||||
|
||||
getCellText(index, column) {
|
||||
return `${column.id} ${this.values[index]}`;
|
||||
}
|
||||
|
||||
isContainer() {
|
||||
return false;
|
||||
}
|
||||
|
||||
isContainerOpen() {
|
||||
return false;
|
||||
}
|
||||
|
||||
selectionChanged() {}
|
||||
|
||||
setTree() {}
|
||||
}
|
||||
|
||||
let tree = document.getElementById("testTree");
|
||||
let table = tree.table;
|
||||
table.setListBoxID("testList");
|
||||
table.listbox.setAttribute("rows", "test-listrow");
|
||||
table.listbox.addEventListener("select", () => {
|
||||
console.log(
|
||||
"select event, selected indices:",
|
||||
table.listbox.selectedIndices
|
||||
);
|
||||
});
|
||||
table.listbox.view = new TestView(0, 50);
|
||||
});
|
||||
|
|
|
@ -1,3 +1,8 @@
|
|||
<?xml version="1.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/. -->
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
|
@ -5,46 +10,36 @@
|
|||
<title>Test for the tree-view-listbox custom element</title>
|
||||
<link rel="stylesheet" href="chrome://messenger/skin/shared/tree-listbox.css" />
|
||||
<style>
|
||||
#testList {
|
||||
.tree-view-scrollable-container {
|
||||
height: 630px;
|
||||
scroll-behavior: unset;
|
||||
--in-content-button-background: rgba(12, 12, 13, 0.1);
|
||||
--in-content-focus-outline-color: #45a1ff;
|
||||
--in-content-item-selected-text: highlighttext;
|
||||
--in-content-item-selected: highlight;
|
||||
}
|
||||
|
||||
test-listrow {
|
||||
height: 36px;
|
||||
padding: 7px;
|
||||
tr[is="test-listrow"] td > div {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
test-listrow div.d1 {
|
||||
tr[is="test-listrow"] td div.d1 {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
test-listrow div.d1 > div.d2 {
|
||||
line-height: 18px;
|
||||
tr[is="test-listrow"] td div.d1 > div.d2 {
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
test-listrow div.d1 > div.d3 {
|
||||
line-height: 18px;
|
||||
font-size: 13.333px;
|
||||
}
|
||||
|
||||
alternative-listrow {
|
||||
height: 80px;
|
||||
tr[is="test-listrow"] td div.d1 > div.d3 {
|
||||
line-height: 1.2;
|
||||
font-size: 13px;
|
||||
}
|
||||
</style>
|
||||
<script type="module" defer="defer" src="chrome://messenger/content/tree-view-listbox.mjs"></script>
|
||||
<script defer="defer" src="treeViewListbox.js"></script>
|
||||
<script type="module" src="chrome://messenger/content/tree-view-listbox.mjs"></script>
|
||||
<script src="treeViewListbox.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<input id="before" placeholder="something to focus on" />
|
||||
<tree-view-listbox id="testList" tabindex="0" rows="test-listrow"></tree-view-listbox>
|
||||
<tree-view id="testTree"/>
|
||||
<input id="after" placeholder="something to focus on" />
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -1,94 +1,110 @@
|
|||
/* 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/. */
|
||||
|
||||
/* globals PROTO_TREE_VIEW */
|
||||
|
||||
class TestCardRow extends customElements.get("tree-view-listrow") {
|
||||
static ROW_HEIGHT = 30;
|
||||
// FIXME: Wrap the whole method around the document load listener to prevent the
|
||||
// undefined state of the "tree-view-listrow" element. This is due to the .mjs
|
||||
// nature of the class file.
|
||||
window.addEventListener("load", () => {
|
||||
class TestCardRow extends customElements.get("tree-view-listrow") {
|
||||
static ROW_HEIGHT = 30;
|
||||
|
||||
connectedCallback() {
|
||||
if (this.hasConnected) {
|
||||
return;
|
||||
connectedCallback() {
|
||||
if (this.hasConnected) {
|
||||
return;
|
||||
}
|
||||
|
||||
super.connectedCallback();
|
||||
|
||||
this.cell = this.appendChild(document.createElement("td"));
|
||||
let container = this.cell.appendChild(document.createElement("div"));
|
||||
|
||||
this.twisty = container.appendChild(document.createElement("div"));
|
||||
this.twisty.classList.add("twisty");
|
||||
|
||||
this.d2 = container.appendChild(document.createElement("div"));
|
||||
this.d2.classList.add("d2");
|
||||
}
|
||||
|
||||
super.connectedCallback();
|
||||
get index() {
|
||||
return super.index;
|
||||
}
|
||||
|
||||
this.twisty = this.appendChild(document.createElement("div"));
|
||||
this.twisty.classList.add("twisty");
|
||||
set index(index) {
|
||||
super.index = index;
|
||||
this.id = this.view.getRowProperties(index);
|
||||
this.classList.remove("level0", "level1", "level2");
|
||||
this.classList.add(`level${this.view.getLevel(index)}`);
|
||||
this.d2.textContent = this.view.getCellText(index, { id: "text" });
|
||||
}
|
||||
}
|
||||
customElements.define("test-listrow", TestCardRow, { extends: "tr" });
|
||||
|
||||
this.d2 = this.appendChild(document.createElement("div"));
|
||||
this.d2.classList.add("d2");
|
||||
class TreeItem {
|
||||
_children = [];
|
||||
|
||||
constructor(id, text, open = false, level = 0) {
|
||||
this._id = id;
|
||||
this._text = text;
|
||||
this._open = open;
|
||||
this._level = level;
|
||||
}
|
||||
|
||||
getText() {
|
||||
return this._text;
|
||||
}
|
||||
|
||||
get open() {
|
||||
return this._open;
|
||||
}
|
||||
|
||||
get level() {
|
||||
return this._level;
|
||||
}
|
||||
|
||||
get children() {
|
||||
return this._children;
|
||||
}
|
||||
|
||||
getProperties() {
|
||||
return this._id;
|
||||
}
|
||||
|
||||
addChild(treeItem) {
|
||||
treeItem._parent = this;
|
||||
treeItem._level = this._level + 1;
|
||||
this.children.push(treeItem);
|
||||
}
|
||||
}
|
||||
|
||||
get index() {
|
||||
return super.index;
|
||||
}
|
||||
let testView = new PROTO_TREE_VIEW();
|
||||
testView._rowMap.push(new TreeItem("row-1", "Item with no children"));
|
||||
testView._rowMap.push(new TreeItem("row-2", "Item with children"));
|
||||
testView._rowMap.push(new TreeItem("row-3", "Item with grandchildren"));
|
||||
testView._rowMap[1].addChild(new TreeItem("row-2-1", "First child"));
|
||||
testView._rowMap[1].addChild(new TreeItem("row-2-2", "Second child"));
|
||||
testView._rowMap[2].addChild(new TreeItem("row-3-1", "First child"));
|
||||
testView._rowMap[2].children[0].addChild(
|
||||
new TreeItem("row-3-1-1", "First grandchild")
|
||||
);
|
||||
testView._rowMap[2].children[0].addChild(
|
||||
new TreeItem("row-3-1-2", "Second grandchild")
|
||||
);
|
||||
testView.toggleOpenState(1);
|
||||
testView.toggleOpenState(4);
|
||||
testView.toggleOpenState(5);
|
||||
|
||||
set index(index) {
|
||||
super.index = index;
|
||||
this.id = this.view.getRowProperties(index);
|
||||
this.classList.remove("level0", "level1", "level2");
|
||||
this.classList.add(`level${this.view.getLevel(index)}`);
|
||||
this.d2.textContent = this.view.getCellText(index, { id: "text" });
|
||||
}
|
||||
}
|
||||
customElements.define("test-listrow", TestCardRow);
|
||||
|
||||
class TreeItem {
|
||||
_children = [];
|
||||
|
||||
constructor(id, text, open = false, level = 0) {
|
||||
this._id = id;
|
||||
this._text = text;
|
||||
this._open = open;
|
||||
this._level = level;
|
||||
}
|
||||
|
||||
getText() {
|
||||
return this._text;
|
||||
}
|
||||
|
||||
get open() {
|
||||
return this._open;
|
||||
}
|
||||
|
||||
get level() {
|
||||
return this._level;
|
||||
}
|
||||
|
||||
get children() {
|
||||
return this._children;
|
||||
}
|
||||
|
||||
getProperties() {
|
||||
return this._id;
|
||||
}
|
||||
|
||||
addChild(treeItem) {
|
||||
treeItem._parent = this;
|
||||
treeItem._level = this._level + 1;
|
||||
this.children.push(treeItem);
|
||||
}
|
||||
}
|
||||
|
||||
let testView = new PROTO_TREE_VIEW();
|
||||
testView._rowMap.push(new TreeItem("row-1", "Item with no children"));
|
||||
testView._rowMap.push(new TreeItem("row-2", "Item with children"));
|
||||
testView._rowMap.push(new TreeItem("row-3", "Item with grandchildren"));
|
||||
testView._rowMap[1].addChild(new TreeItem("row-2-1", "First child"));
|
||||
testView._rowMap[1].addChild(new TreeItem("row-2-2", "Second child"));
|
||||
testView._rowMap[2].addChild(new TreeItem("row-3-1", "First child"));
|
||||
testView._rowMap[2].children[0].addChild(
|
||||
new TreeItem("row-3-1-1", "First grandchild")
|
||||
);
|
||||
testView._rowMap[2].children[0].addChild(
|
||||
new TreeItem("row-3-1-2", "Second grandchild")
|
||||
);
|
||||
testView.toggleOpenState(1);
|
||||
testView.toggleOpenState(4);
|
||||
testView.toggleOpenState(5);
|
||||
|
||||
window.addEventListener("load", () => {
|
||||
let list = document.getElementById("testList");
|
||||
list.addEventListener("select", event => {
|
||||
console.log("select event, selected indices:", list.selectedIndices);
|
||||
let tree = document.getElementById("testTree");
|
||||
let table = tree.table;
|
||||
table.setListBoxID("testList");
|
||||
table.listbox.setAttribute("rows", "test-listrow");
|
||||
table.listbox.addEventListener("select", () => {
|
||||
console.log(
|
||||
"select event, selected indices:",
|
||||
table.listbox.selectedIndices
|
||||
);
|
||||
});
|
||||
list.view = testView;
|
||||
table.listbox.view = testView;
|
||||
});
|
||||
|
|
|
@ -5,18 +5,12 @@
|
|||
<title>Test for the tree-view-listbox custom element</title>
|
||||
<link rel="stylesheet" href="chrome://messenger/skin/shared/tree-listbox.css" />
|
||||
<style>
|
||||
#testList {
|
||||
.tree-view-scrollable-container {
|
||||
height: 630px;
|
||||
scroll-behavior: unset;
|
||||
--in-content-button-background: rgba(12, 12, 13, 0.1);
|
||||
--in-content-focus-outline-color: #45a1ff;
|
||||
--in-content-item-selected-text: highlighttext;
|
||||
--in-content-item-selected: highlight;
|
||||
}
|
||||
|
||||
test-listrow {
|
||||
height: 20px;
|
||||
padding: 5px;
|
||||
tr[is="test-listrow"] td > div {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
@ -26,19 +20,19 @@
|
|||
height: 1em;
|
||||
}
|
||||
|
||||
test-listrow.children div.twisty {
|
||||
tr[is="test-listrow"].children div.twisty {
|
||||
background-color: green;
|
||||
}
|
||||
|
||||
test-listrow.children.collapsed div.twisty {
|
||||
tr[is="test-listrow"].children.collapsed div.twisty {
|
||||
background-color: red;
|
||||
}
|
||||
|
||||
test-listrow.level1 .d2 {
|
||||
tr[is="test-listrow"].level1 .d2 {
|
||||
padding-inline-start: 1em;
|
||||
}
|
||||
|
||||
test-listrow.level2 .d2 {
|
||||
tr[is="test-listrow"].level2 .d2 {
|
||||
padding-inline-start: 2em;
|
||||
}
|
||||
</style>
|
||||
|
@ -47,6 +41,6 @@
|
|||
<script defer="defer" src="treeViewListbox2.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<tree-view-listbox id="testList" tabindex="0" rows="test-listrow"></tree-view-listbox>
|
||||
<tree-view id="testTree"/>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -1350,6 +1350,11 @@ class AbCardListrow extends customElements.get("tree-view-listrow") {
|
|||
}
|
||||
customElements.define("ab-card-listrow", AbCardListrow, { extends: "tr" });
|
||||
|
||||
/**
|
||||
* A row in the table list of cards.
|
||||
*
|
||||
* @augments {TreeViewListrow}
|
||||
*/
|
||||
class AbTableCardListrow extends customElements.get("tree-view-listrow") {
|
||||
static ROW_HEIGHT = 22;
|
||||
|
||||
|
@ -1861,9 +1866,7 @@ var cardsPane = {
|
|||
break;
|
||||
}
|
||||
|
||||
for (let element of document.getElementById("cardsPlaceholder").children) {
|
||||
element.hidden = !idsToShow.includes(element.id);
|
||||
}
|
||||
this.cardsList.updatePlaceholders(idsToShow);
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -2114,7 +2117,9 @@ var cardsPane = {
|
|||
if (event.target == this.cardsList) {
|
||||
row = this.cardsList.getRowAtIndex(this.cardsList.currentIndex);
|
||||
} else {
|
||||
row = event.target.closest("ab-card-listrow, ab-table-card-listrow");
|
||||
row = event.target.closest(
|
||||
`tr[is="ab-card-listrow"], tr[is="ab-table-card-listrow"]`
|
||||
);
|
||||
}
|
||||
if (!row) {
|
||||
return;
|
||||
|
@ -2320,7 +2325,9 @@ var cardsPane = {
|
|||
) {
|
||||
return;
|
||||
}
|
||||
let row = event.target.closest("ab-card-listrow, ab-table-card-listrow");
|
||||
let row = event.target.closest(
|
||||
`tr[is="ab-card-listrow"], tr[is="ab-table-card-listrow"]`
|
||||
);
|
||||
if (row) {
|
||||
this._activateRow(row.index);
|
||||
}
|
||||
|
@ -2362,7 +2369,9 @@ var cardsPane = {
|
|||
return MailServices.headerParser.makeMimeAddress(card.displayName, email);
|
||||
}
|
||||
|
||||
let row = event.target.closest("ab-card-listrow, ab-table-card-listrow");
|
||||
let row = event.target.closest(
|
||||
`tr[is="ab-card-listrow"], tr[is="ab-table-card-listrow"]`
|
||||
);
|
||||
if (!row) {
|
||||
event.preventDefault();
|
||||
return;
|
||||
|
|
|
@ -119,26 +119,26 @@
|
|||
</button>
|
||||
</div>
|
||||
|
||||
<tree-view id="addressBookTree"/>
|
||||
|
||||
<div id="cardsPlaceholder">
|
||||
<div id="placeholderEmptyBook"
|
||||
hidden="hidden"
|
||||
data-l10n-id="about-addressbook-placeholder-empty-book"></div>
|
||||
<button id="placeholderCreateContact"
|
||||
class="icon-button"
|
||||
hidden="hidden"
|
||||
data-l10n-id="about-addressbook-placeholder-new-contact"></button>
|
||||
<div id="placeholderSearchOnly"
|
||||
hidden="hidden"
|
||||
data-l10n-id="about-addressbook-placeholder-search-only"></div>
|
||||
<div id="placeholderSearching"
|
||||
hidden="hidden"
|
||||
data-l10n-id="about-addressbook-placeholder-searching"></div>
|
||||
<div id="placeholderNoSearchResults"
|
||||
hidden="hidden"
|
||||
data-l10n-id="about-addressbook-placeholder-no-search-results"></div>
|
||||
</div>
|
||||
<tree-view id="addressBookTree">
|
||||
<slot name="placeholders">
|
||||
<div id="placeholderEmptyBook"
|
||||
hidden="hidden"
|
||||
data-l10n-id="about-addressbook-placeholder-empty-book"></div>
|
||||
<button id="placeholderCreateContact"
|
||||
class="icon-button"
|
||||
hidden="hidden"
|
||||
data-l10n-id="about-addressbook-placeholder-new-contact"></button>
|
||||
<div id="placeholderSearchOnly"
|
||||
hidden="hidden"
|
||||
data-l10n-id="about-addressbook-placeholder-search-only"></div>
|
||||
<div id="placeholderSearching"
|
||||
hidden="hidden"
|
||||
data-l10n-id="about-addressbook-placeholder-searching"></div>
|
||||
<div id="placeholderNoSearchResults"
|
||||
hidden="hidden"
|
||||
data-l10n-id="about-addressbook-placeholder-no-search-results"></div>
|
||||
</slot>
|
||||
</tree-view>
|
||||
</div>
|
||||
<!-- We will dynamically switch this splitter to be horizontal or vertical and
|
||||
affect the cardsPane or detailsPane based on the required layout. -->
|
||||
|
|
|
@ -40,7 +40,7 @@ add_task(async function test_additions_and_removals() {
|
|||
let contactB1 = bookB.addCard(createContact("contact", "B1"));
|
||||
|
||||
let abWindow = await openAddressBookWindow();
|
||||
let cardsList = abWindow.document.getElementById("cards");
|
||||
let cardsList = abWindow.cardsPane.cardsList;
|
||||
|
||||
await openAllAddressBooks();
|
||||
info("Performing check #1");
|
||||
|
@ -248,7 +248,7 @@ add_task(async function test_name_column() {
|
|||
book.addCard(createContact("echo", "november", "uniform"));
|
||||
|
||||
let abWindow = await openAddressBookWindow();
|
||||
let cardsList = abWindow.document.getElementById("cards");
|
||||
let cardsList = abWindow.cardsPane.cardsList;
|
||||
|
||||
// Check the format is display name, ascending.
|
||||
Assert.equal(
|
||||
|
@ -818,32 +818,32 @@ add_task(async function test_context_menu_delete() {
|
|||
|
||||
add_task(async function test_layout() {
|
||||
function checkColumns(visibleColumns, sortColumn, sortDirection) {
|
||||
let visibleHeaders = cardsHeader.querySelectorAll("button:not([hidden])");
|
||||
let visibleHeaders = cardsHeader.querySelectorAll(
|
||||
`th[is="tree-view-table-header-cell"]:not([hidden])`
|
||||
);
|
||||
Assert.deepEqual(
|
||||
Array.from(visibleHeaders, b => b.value),
|
||||
Array.from(visibleHeaders, h => h.id),
|
||||
visibleColumns,
|
||||
"visible columns are correct"
|
||||
);
|
||||
|
||||
for (let header of visibleHeaders) {
|
||||
let button = header.querySelector("button");
|
||||
Assert.equal(
|
||||
header.classList.contains("ascending"),
|
||||
header.value == sortColumn && sortDirection == "ascending",
|
||||
`${header.value} header is ascending`
|
||||
button.classList.contains("ascending"),
|
||||
header.id == sortColumn && sortDirection == "ascending",
|
||||
`${header.id} header is ascending`
|
||||
);
|
||||
Assert.equal(
|
||||
header.classList.contains("descending"),
|
||||
header.value == sortColumn && sortDirection == "descending",
|
||||
`${header.value} header is descending`
|
||||
button.classList.contains("descending"),
|
||||
header.id == sortColumn && sortDirection == "descending",
|
||||
`${header.id} header is descending`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function checkRowHeight(height) {
|
||||
Assert.equal(cardsList.getRowAtIndex(0).clientHeight, height);
|
||||
Assert.equal(cardsList.getRowAtIndex(0).style.top, "0px");
|
||||
Assert.equal(cardsList.getRowAtIndex(1).clientHeight, height);
|
||||
Assert.equal(cardsList.getRowAtIndex(1).style.top, `${height}px`);
|
||||
}
|
||||
|
||||
Services.prefs.setIntPref("mail.uidensity", 0);
|
||||
|
@ -862,8 +862,8 @@ add_task(async function test_layout() {
|
|||
|
||||
let abWindow = await openAddressBookWindow();
|
||||
let abDocument = abWindow.document;
|
||||
let cardsHeader = abDocument.getElementById("cardsHeader");
|
||||
let cardsList = abDocument.getElementById("cards");
|
||||
let cardsList = abWindow.cardsPane.cardsList;
|
||||
let cardsHeader = abWindow.cardsPane.table.header;
|
||||
let sharedSplitter = abDocument.getElementById("sharedSplitter");
|
||||
|
||||
// Sanity check.
|
||||
|
@ -928,7 +928,7 @@ add_task(async function test_layout() {
|
|||
// Click the email addresses header to sort.
|
||||
|
||||
EventUtils.synthesizeMouseAtCenter(
|
||||
cardsHeader.querySelector(`[value="EmailAddresses"]`),
|
||||
cardsHeader.querySelector(`[id="EmailAddressesButton"]`),
|
||||
{},
|
||||
abWindow
|
||||
);
|
||||
|
@ -947,7 +947,7 @@ add_task(async function test_layout() {
|
|||
// Click the email addresses header again to flip the sort.
|
||||
|
||||
EventUtils.synthesizeMouseAtCenter(
|
||||
cardsHeader.querySelector(`[value="EmailAddresses"]`),
|
||||
cardsHeader.querySelector(`[id="EmailAddressesButton"]`),
|
||||
{},
|
||||
abWindow
|
||||
);
|
||||
|
@ -965,9 +965,9 @@ add_task(async function test_layout() {
|
|||
|
||||
// Add a column.
|
||||
|
||||
await showSortMenu("toggle", "Title");
|
||||
await showPickerMenu("toggle", "Title");
|
||||
await TestUtils.waitForCondition(
|
||||
() => !cardsHeader.querySelector(`[value="Title"]`).hidden
|
||||
() => !cardsHeader.querySelector(`[id="Title"]`).hidden
|
||||
);
|
||||
checkColumns(
|
||||
["GeneratedName", "EmailAddresses", "PhoneNumbers", "Addresses", "Title"],
|
||||
|
@ -977,9 +977,9 @@ add_task(async function test_layout() {
|
|||
|
||||
// Remove a column.
|
||||
|
||||
await showSortMenu("toggle", "Addresses");
|
||||
await showPickerMenu("toggle", "Addresses");
|
||||
await TestUtils.waitForCondition(
|
||||
() => cardsHeader.querySelector(`[value="Addresses"]`).hidden
|
||||
() => cardsHeader.querySelector(`[id="Addresses"]`).hidden
|
||||
);
|
||||
checkColumns(
|
||||
["GeneratedName", "EmailAddresses", "PhoneNumbers", "Title"],
|
||||
|
@ -1001,8 +1001,8 @@ add_task(async function test_layout() {
|
|||
|
||||
abWindow = await openAddressBookWindow();
|
||||
abDocument = abWindow.document;
|
||||
cardsHeader = abDocument.getElementById("cardsHeader");
|
||||
cardsList = abDocument.getElementById("cards");
|
||||
cardsList = abWindow.cardsPane.cardsList;
|
||||
cardsHeader = abWindow.cardsPane.table.header;
|
||||
sharedSplitter = abDocument.getElementById("sharedSplitter");
|
||||
|
||||
Assert.ok(
|
||||
|
@ -1149,17 +1149,16 @@ add_task(async function test_list_table_layout() {
|
|||
book.addMailList(list);
|
||||
|
||||
let abWindow = await openAddressBookWindow();
|
||||
let abDocument = abWindow.document;
|
||||
let cardsList = abWindow.cardsPane.cardsList;
|
||||
let cardsHeader = abDocument.getElementById("cardsHeader");
|
||||
let cardsHeader = abWindow.cardsPane.table.header;
|
||||
|
||||
// Switch layout to table.
|
||||
|
||||
await toggleLayout();
|
||||
|
||||
await showSortMenu("toggle", "addrbook");
|
||||
await showPickerMenu("toggle", "addrbook");
|
||||
await TestUtils.waitForCondition(
|
||||
() => !cardsHeader.querySelector(`[value="addrbook"]`).hidden
|
||||
() => !cardsHeader.querySelector(`[id="addrbook"]`).hidden
|
||||
);
|
||||
|
||||
// Check for the contact that the column is shown.
|
||||
|
@ -1202,9 +1201,8 @@ add_task(async function test_list_all_address_book() {
|
|||
secondBook.addMailList(list);
|
||||
|
||||
let abWindow = await openAddressBookWindow();
|
||||
let abDocument = abWindow.document;
|
||||
let cardsList = abWindow.cardsPane.cardsList;
|
||||
let cardsHeader = abDocument.getElementById("cardsHeader");
|
||||
let cardsHeader = abWindow.cardsPane.table.header;
|
||||
|
||||
info("Check that no address book suffix is present.");
|
||||
Assert.ok(
|
||||
|
@ -1223,7 +1221,7 @@ add_task(async function test_list_all_address_book() {
|
|||
info("Toggle the option to show address books.");
|
||||
await showSortMenu("toggle", "addrbook");
|
||||
await TestUtils.waitForCondition(
|
||||
() => !cardsHeader.querySelector(`[value="addrbook"]`).hidden
|
||||
() => !cardsHeader.querySelector(`[id="addrbook"]`).hidden
|
||||
);
|
||||
|
||||
Assert.ok(
|
||||
|
|
|
@ -968,7 +968,7 @@ add_task(async function test_total_address_book_count() {
|
|||
// Delete a contact an check that the count updates.
|
||||
let promptPromise = BrowserTestUtils.promiseAlertDialog("accept");
|
||||
let deletedPromise = TestUtils.topicObserved("addrbook-contact-deleted");
|
||||
let cards = abDocument.getElementById("cards");
|
||||
let cards = abWindow.cardsPane.cardsList;
|
||||
EventUtils.synthesizeMouseAtCenter(cards.getRowAtIndex(0), {}, abWindow);
|
||||
EventUtils.synthesizeKey("VK_DELETE", {}, abWindow);
|
||||
await promptPromise;
|
||||
|
|
|
@ -52,7 +52,7 @@ add_task(async () => {
|
|||
let abDocument = abWindow.document;
|
||||
|
||||
let searchBox = abDocument.getElementById("searchInput");
|
||||
let cardsList = abDocument.getElementById("cards");
|
||||
let cardsList = abWindow.cardsPane.cardsList;
|
||||
let noSearchResults = abDocument.getElementById("placeholderNoSearchResults");
|
||||
let detailsPane = abDocument.getElementById("detailsPane");
|
||||
|
||||
|
|
|
@ -60,7 +60,7 @@ add_task(async () => {
|
|||
|
||||
let abDocument = abWindow.document;
|
||||
let searchBox = abDocument.getElementById("searchInput");
|
||||
let cardsList = abDocument.getElementById("cards");
|
||||
let cardsList = abWindow.cardsPane.cardsList;
|
||||
|
||||
Assert.equal(
|
||||
abDocument.activeElement,
|
||||
|
|
|
@ -206,7 +206,7 @@ async function createMailingListWithUI(mlParent, mlName) {
|
|||
function checkDirectoryDisplayed(directory) {
|
||||
let abWindow = getAddressBookWindow();
|
||||
let booksList = abWindow.document.getElementById("books");
|
||||
let cardsList = abWindow.document.getElementById("cards");
|
||||
let cardsList = abWindow.cardsPane.cardsList;
|
||||
|
||||
if (directory) {
|
||||
Assert.equal(
|
||||
|
@ -249,7 +249,7 @@ function checkNamesListed(...expectedNames) {
|
|||
|
||||
function checkPlaceholders(expectedVisible = []) {
|
||||
let abWindow = getAddressBookWindow();
|
||||
let placeholder = abWindow.document.getElementById("cardsPlaceholder");
|
||||
let placeholder = abWindow.cardsPane.cardsList.placeholder;
|
||||
|
||||
if (!expectedVisible.length) {
|
||||
Assert.ok(
|
||||
|
@ -288,6 +288,28 @@ async function showSortMenu(name, value) {
|
|||
await hiddenPromise;
|
||||
}
|
||||
|
||||
async function showPickerMenu(name, value) {
|
||||
let abWindow = getAddressBookWindow();
|
||||
let cardsHeader = abWindow.cardsPane.table.header;
|
||||
let pickerButton = cardsHeader.querySelector(
|
||||
`th[is="tree-view-table-column-picker"] button`
|
||||
);
|
||||
let menupopup = cardsHeader.querySelector(
|
||||
`th[is="tree-view-table-column-picker"] menupopup`
|
||||
);
|
||||
let shownPromise = BrowserTestUtils.waitForEvent(menupopup, "popupshown");
|
||||
EventUtils.synthesizeMouseAtCenter(pickerButton, {}, abWindow);
|
||||
await shownPromise;
|
||||
let hiddenPromise = BrowserTestUtils.waitForEvent(menupopup, "popuphidden");
|
||||
menupopup.activateItem(
|
||||
menupopup.querySelector(`[name="${name}"][value="${value}"]`)
|
||||
);
|
||||
if (name == "toggle") {
|
||||
menupopup.hidePopup();
|
||||
}
|
||||
await hiddenPromise;
|
||||
}
|
||||
|
||||
async function toggleLayout() {
|
||||
let abWindow = getAddressBookWindow();
|
||||
let abDocument = abWindow.document;
|
||||
|
|
|
@ -397,40 +397,6 @@ body:not(.layout-table).all-ab-selected ~ menupopup#sortContext >
|
|||
fill: currentColor;
|
||||
}
|
||||
|
||||
#cardsPlaceholder {
|
||||
position: absolute;
|
||||
inset: 120px 0 auto;
|
||||
display: none;
|
||||
padding: 0 40px;
|
||||
box-sizing: border-box;
|
||||
color: var(--in-content-deemphasized-text);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#cards:empty + #cardsPlaceholder {
|
||||
display: block;
|
||||
}
|
||||
|
||||
#cardsPlaceholder > div {
|
||||
margin-block-end: 0.25em;
|
||||
font-size: 1.3rem;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
#cardsPlaceholder > div::before {
|
||||
content: "";
|
||||
display: block;
|
||||
height: 32px;
|
||||
margin-block-end: 9px;
|
||||
background-position: center top;
|
||||
background-size: contain;
|
||||
background-repeat: no-repeat;
|
||||
-moz-context-properties: fill, stroke, fill-opacity;
|
||||
fill: color-mix(in srgb, currentColor 20%, transparent);
|
||||
stroke: currentColor;
|
||||
fill-opacity: var(--toolbarbutton-icon-fill-opacity);
|
||||
}
|
||||
|
||||
#placeholderEmptyBook::before,
|
||||
#placeholderSearchOnly::before {
|
||||
background-image: var(--addressbook-tree-list);
|
||||
|
@ -456,6 +422,8 @@ body:not(.layout-table).all-ab-selected ~ menupopup#sortContext >
|
|||
tr[is="ab-card-listrow"] td > .card-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
max-height: inherit;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.selected-card {
|
||||
|
|
|
@ -92,6 +92,8 @@ table[is="tree-view-table"] {
|
|||
table-layout: fixed;
|
||||
flex: 1 0 100%;
|
||||
border-spacing: 0;
|
||||
line-height: 1;
|
||||
font-size: 1.05rem;
|
||||
}
|
||||
|
||||
table[is="tree-view-table"] td {
|
||||
|
@ -230,6 +232,44 @@ table[is="tree-view-table"] td div {
|
|||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
/* Placeholder */
|
||||
|
||||
slot[name="placeholders"] {
|
||||
position: absolute;
|
||||
display: none;
|
||||
box-sizing: border-box;
|
||||
inset: 120px 0 auto;
|
||||
padding: 0 40px;
|
||||
color: var(--in-content-deemphasized-text);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
slot[name="placeholders"].show {
|
||||
display: block;
|
||||
}
|
||||
|
||||
slot[name="placeholders"] > div{
|
||||
font-size: 1.5rem;
|
||||
line-height: 1.2;
|
||||
font-weight: 600;
|
||||
margin-block-end: 12px;
|
||||
text-shadow: 0 1px 0px var(--in-content-page-background);
|
||||
}
|
||||
|
||||
slot[name="placeholders"] div::before {
|
||||
content: "";
|
||||
display: block;
|
||||
height: 32px;
|
||||
margin-block-end: 9px;
|
||||
background-position: center top;
|
||||
background-size: contain;
|
||||
background-repeat: no-repeat;
|
||||
-moz-context-properties: fill, stroke, fill-opacity;
|
||||
fill: color-mix(in srgb, currentColor 20%, transparent);
|
||||
stroke: currentColor;
|
||||
fill-opacity: var(--toolbarbutton-icon-fill-opacity);
|
||||
}
|
||||
|
||||
/* Transitions and animations */
|
||||
|
||||
@media (prefers-reduced-motion: no-preference) {
|
||||
|
|
Загрузка…
Ссылка в новой задаче