Show the focus ring on the focused collection-tree row

The collection tree's row rendering never applied the 'focused' class, so the
dotted focus ring (already styled in virtualized-table.scss) didn't appear when
moving the focus row without selecting it (macOS Cmd-arrow). Toggle it like the
item tree does.
This commit is contained in:
Dan Stillman 2026-06-16 12:46:07 -04:00
parent ea68385428
commit 212f5aed0d
2 changed files with 24 additions and 0 deletions

View file

@ -316,6 +316,8 @@ var CollectionTree = class CollectionTree extends LibraryTree {
div.classList.toggle('selected', selection.isSelected(index));
div.classList.toggle('first-selected', selection.isFirstRowOfSelectionBlock(index));
div.classList.toggle('last-selected', selection.isLastRowOfSelectionBlock(index));
// Focus ring on the focused-but-unselected row (e.g., macOS Cmd-arrow navigation)
div.classList.toggle('focused', selection.focused == index);
div.classList.toggle('highlighted', this._highlightedRows.has(treeRow.id));
div.classList.toggle('drop', this._dropRow == index);
div.classList.toggle('flashing', this._flashingRow == index);

View file

@ -756,6 +756,28 @@ describe("Zotero.CollectionTree", function () {
assert.isTrue(cv.selection.isSelected(cv.getRowIndexByID("C" + c1.id)));
});
it("should mark the focused-but-unselected row for the focus ring", async function () {
let c1 = await createDataObject('collection');
let c2 = await createDataObject('collection');
await cv.selectByID("C" + c1.id);
let r2 = cv.getRowIndexByID("C" + c2.id);
// Move focus to c2 without selecting it (macOS Cmd-arrow style)
cv.tree._onSelection(r2, false, false, true);
assert.equal(cv.selection.focused, r2);
assert.isFalse(cv.selection.isSelected(r2));
// The focused (unselected) row gets the 'focused' class that drives the
// dotted focus ring (rendering is async, so wait for it)
await waitForCallback(() => {
let n = win.document.getElementById(`${cv.id}-row-${r2}`);
return n && n.classList.contains('focused');
}, 50, 20);
let node = win.document.getElementById(`${cv.id}-row-${r2}`);
assert.isTrue(node.classList.contains('focused'));
assert.isFalse(node.classList.contains('selected'));
});
it("should not leave a stale multi-selection after filtering", async function () {
let cA = await createDataObject('collection', { name: 'filterAAA' });
let cB = await createDataObject('collection', { name: 'filterBBB' });