Skip to content

Commit

Permalink
restore scroll position on initial navigation (#3572)
Browse files Browse the repository at this point in the history
In the past, when navigating back from an external or dead page
to a live page, the scroll position would not be restored.
  • Loading branch information
SteffenDE authored Dec 16, 2024
1 parent 7268954 commit c40a742
Show file tree
Hide file tree
Showing 4 changed files with 62 additions and 1 deletion.
6 changes: 5 additions & 1 deletion assets/js/phoenix_live_view/live_socket.js
Original file line number Diff line number Diff line change
Expand Up @@ -362,7 +362,11 @@ export default class LiveSocket {
view.setHref(this.getHref())
view.joinDead()
if(!this.main){ this.main = view }
window.requestAnimationFrame(() => view.execNewMounted())
window.requestAnimationFrame(() => {
view.execNewMounted()
// restore scroll position when navigating from an external / non-live page
this.maybeScroll(history.state?.scroll)
})
}
}

Expand Down
29 changes: 29 additions & 0 deletions test/e2e/support/navigation.ex
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@ defmodule Phoenix.LiveViewTest.E2E.Navigation.Layout do
<.link navigate="/stream" style="background-color: #f1f5f9; padding: 0.5rem;">
LiveView (other session)
</.link>
<.link navigate="/navigation/dead" style="background-color: #f1f5f9; padding: 0.5rem;">
Dead View
</.link>
</div>
<div style="margin-left: 22rem; flex: 1; padding: 2rem;">
Expand Down Expand Up @@ -165,3 +169,28 @@ defmodule Phoenix.LiveViewTest.E2E.Navigation.BLive do
"""
end
end

defmodule Phoenix.LiveViewTest.E2E.Navigation.Dead do
use Phoenix.Controller,
formats: [:html],
layouts: [html: {Phoenix.LiveViewTest.E2E.Navigation.Layout, :live}]

import Phoenix.Component, only: [sigil_H: 2]

def index(conn, _params) do
assigns = %{}

conn
|> render(:index)
end
end

defmodule Phoenix.LiveViewTest.E2E.Navigation.DeadHTML do
use Phoenix.Component

def index(assigns) do
~H"""
<h1>Dead view</h1>
"""
end
end
1 change: 1 addition & 0 deletions test/e2e/test_helper.exs
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,7 @@ defmodule Phoenix.LiveViewTest.E2E.Router do
live "/a", Phoenix.LiveViewTest.E2E.Navigation.ALive
live "/b", Phoenix.LiveViewTest.E2E.Navigation.BLive, :index
live "/b/:id", Phoenix.LiveViewTest.E2E.Navigation.BLive, :show
get "/dead", Phoenix.LiveViewTest.E2E.Navigation.Dead, :index
end
end

Expand Down
27 changes: 27 additions & 0 deletions test/e2e/tests/navigation.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,33 @@ test("scrolls hash el into view after live navigation (issue #3452)", async ({pa
expect(scrollTop).toBeLessThanOrEqual(offset + 500)
})

test("restores scroll position when navigating from dead view", async ({page}) => {
await page.goto("/navigation/b")
await syncLV(page)

await expect(page.locator("#items")).toContainText("Item 42")

expect(await page.evaluate(() => document.documentElement.scrollTop)).toEqual(0)
const offset = (await page.locator("#items-item-42").evaluate((el) => el.offsetTop)) - 200
await page.evaluate((offset) => window.scrollTo(0, offset), offset)
// LiveView only updates the scroll position every 100ms
await page.waitForTimeout(150)

await page.getByRole("link", {name: "Dead"}).click()
await page.waitForURL("/navigation/dead")

await page.goBack()
await syncLV(page)

// scroll position is restored
await expect.poll(
async () => {
return await page.evaluate(() => document.documentElement.scrollTop)
},
{message: "scrollTop not restored", timeout: 5000}
).toBe(offset)
})

test("navigating all the way back works without remounting (only patching)", async ({page}) => {
await page.goto("/navigation/a")
await syncLV(page)
Expand Down

0 comments on commit c40a742

Please sign in to comment.