fix(tasks): preserve GitHub list scroll restoration (#17547)

This commit is contained in:
Jinjing
2026-08-30 19:25:46 -07:00
committed by GitHub
parent ce9482b4db
commit 68bb227a11
3 changed files with 34 additions and 1 deletions
+5 -1
View File
@@ -538,7 +538,10 @@ export default function TaskPage(): React.JSX.Element {
useLayoutEffect(() => {
const target = pendingGithubScrollRestoreRef.current
if (target === null || !pages[currentPage]) {
// Start observing as soon as the list mounts; its page rows may be committed
// by a later render, and the restore helper will retry when they appear.
// Keep the target armed while the detail route is transitioning and the list is still mounted.
if (target === null || pageData.openGitHubWorkItem) {
return
}
return startGitHubListScrollRestore({
@@ -559,6 +562,7 @@ export default function TaskPage(): React.JSX.Element {
currentPage,
dialogWorkItem,
githubResumeContextKey,
pageData.openGitHubWorkItem,
pages,
githubListScrollTopRef,
pendingGithubScrollRestoreRef,
@@ -106,6 +106,28 @@ describe('GitHub task list scroll restore', () => {
expect(applied.at(-1)).toBe(360)
})
it('retries when the rows mount after the restore starts', async () => {
const list = createScrollList(0)
list.element.removeChild(list.rows)
const pendingRestoreRef = ref<number | null>(360)
const restoreWriteRef = ref<GitHubListRestoreWrite | null>(null)
startGitHubListScrollRestore({
target: 360,
scrollElementRef: ref<HTMLElement | null>(list.element),
pendingRestoreRef,
restoreWriteRef,
onScrollTopApplied: () => {}
})
list.setMaxScrollTop(900)
list.element.append(list.rows)
await Promise.resolve()
expect(list.element.scrollTop).toBe(360)
expect(pendingRestoreRef.current).toBeNull()
})
it('keeps the remembered offset armed when the list never becomes tall enough', () => {
const list = createScrollList(0)
const pendingRestoreRef = ref<number | null>(360)
@@ -40,6 +40,7 @@ export function startGitHubListScrollRestore({
restoreWriteRef.current = null
let frame: number | null = null
let observer: ResizeObserver | null = null
let mutationObserver: MutationObserver | null = null
const stop = (): void => {
if (frame !== null) {
window.cancelAnimationFrame(frame)
@@ -47,6 +48,8 @@ export function startGitHubListScrollRestore({
}
observer?.disconnect()
observer = null
mutationObserver?.disconnect()
mutationObserver = null
}
const restore = (): void => {
const element = scrollElementRef.current
@@ -72,6 +75,10 @@ export function startGitHubListScrollRestore({
for (const child of scrollElement.children) {
observer.observe(child)
}
// Rows can mount after this layout effect (for example when a cached page settles),
// so observe child-list changes to cover elements that were not present initially.
mutationObserver = new MutationObserver(restore)
mutationObserver.observe(scrollElement, { childList: true, subtree: true })
restore()
if (pendingRestoreRef.current === target) {
frame = window.requestAnimationFrame(restore)