From 212f5aed0dedc40e96483bc5d49910a73bc94b78 Mon Sep 17 00:00:00 2001 From: Dan Stillman Date: Tue, 16 Jun 2026 12:46:07 -0400 Subject: [PATCH] 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. --- chrome/content/zotero/collectionTree.jsx | 2 ++ test/tests/collectionTreeTest.js | 22 ++++++++++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/chrome/content/zotero/collectionTree.jsx b/chrome/content/zotero/collectionTree.jsx index 25b7041f0f..0f413514e8 100644 --- a/chrome/content/zotero/collectionTree.jsx +++ b/chrome/content/zotero/collectionTree.jsx @@ -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); diff --git a/test/tests/collectionTreeTest.js b/test/tests/collectionTreeTest.js index cf09904995..c3f9e03ee3 100644 --- a/test/tests/collectionTreeTest.js +++ b/test/tests/collectionTreeTest.js @@ -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' });