Skip to content

Commit

Permalink
restore scroll position on initial navigation
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 committed Dec 15, 2024
1 parent 51db662 commit ef258fe
Show file tree
Hide file tree
Showing 4 changed files with 59 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
26 changes: 26 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,25 @@ 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 ef258fe

Please sign in to comment.