Preserve tree scroll position after file review actions#1196
Preserve tree scroll position after file review actions#1196Providence-o wants to merge 1 commit into
Conversation
rebkwok
left a comment
There was a problem hiding this comment.
I apologise for getting Claude to review this (but it is JS, in my defence). I think the approach is good, some suggestions that I think make the UX slightly better and the code less brittle.
I think this should be testable with a playwright test (I've had some success with getting Claude to write those)
| // Only disable browser scroll restoration when there is a position to restore | ||
| if (sessionStorage.getItem("treeScrollTop") && 'scrollRestoration' in history) { | ||
| history.scrollRestoration = 'manual'; | ||
| } |
There was a problem hiding this comment.
There's currently a slightly disconcerting jump in the tree when you click an approve/reject etc button - the tree appears scrolled to the top and then jumps to the remembered position. If we modify this slightly we can hide the tree here, and then unhide it in the restore code below. That means that there's a brief period where the tree isn't visible at all as the page loads, but I think it's a better UX than the jumping.
if (sessionStorage.getItem("treeScrollTop")) {
if ('scrollRestoration' in history) {
history.scrollRestoration = 'manual';
document.head.insertAdjacentHTML("beforeend", "<style id='scroll-restore-style'>#tree-container{visibility:hidden}</style>");
}
}
| treeContainer.scrollTop = parseInt(saved); | ||
| treeContainer.style.visibility = "visible"; | ||
| }, 125); | ||
| } |
There was a problem hiding this comment.
To unhide the tree now, we'd change this to:
if (saved) {
sessionStorage.removeItem("treeScrollTop");
// Wait for browser scroll restoration to complete before applying saved position.
// The browser may reset scrollTop after DOMContentLoaded, so we delay slightly.
setTimeout(() => {
treeContainer.scrollTop = parseInt(saved, 10);
document.getElementById("scroll-restore-style")?.remove();
}, 125);
}
|
|
||
| if (saved) { | ||
| sessionStorage.removeItem("treeScrollTop"); | ||
| treeContainer.style.visibility = "hidden"; |
There was a problem hiding this comment.
I think this is intended to do what I've suggested, hiding the tree altogether first and then unhiding it after the scroll restoration. But when I test it locally, I see the tree at the top position flash briefly.
Claude says this is because DOMContentLoaded fires too late.
Here's the timing:
When the browser encounters <script src="index.js">, it pauses HTML parsing and
fetches the file over the network. During that fetch, the browser may paint
whatever it has already parsed — including #tree-container, visible at the wrong
scroll position. That's the first paint, and it's done before the script even runs.By the time the script executes, fires its DOMContentLoaded listener, and sets
visibility: hidden, the user has already seen that first frame.The style injection at the top of index.js works because it runs synchronously
during script execution — before the browser gets its next paint opportunity. The
browser batches paints; it won't paint again until the current JS task finishes. So
the tree is hidden before the next frame, and stays hidden through the scroll
restoration until the timeout reveals it at the right position.
| const form = event.target.closest("form"); | ||
| if (!form) return; | ||
| const action = form.getAttribute("action") || ""; | ||
| if (action.includes("/approve/") || action.includes("/request_changes/") || action.includes("/reset_review/")) { |
There was a problem hiding this comment.
Do we need to check for the action? Could we cjust check that there's a tree present and assume we want to do this? We've already confirmed that there's a form that could be submitted. i.e.
const tree = document.getElementById("tree-container");
if (tree) sessionStorage.setItem("treeScrollTop", tree.scrollTop);
That has the added bonus of maintaining the scroll position if you're in a workspace with lots of groups, and you submit the context/controls or comments forms, and if we ever change/add actions for that form, it'll continue to work.
|
Since I've got Claude looking at this anyway, here's a playwright test it wrote for me: |
Closes: #906
When reviewing files in a large request, clicking approve/request changes (and undoing these actions) would redirect back to the same file but reset the file tree scroll position to the top.
This fix saves the tree scroll bar position before the form submits and restores it after the page reloads.
There is no test added for this change because the scroll bar position restoration is dependent on a hard-coded delay, making it prone to flakiness.