fix(router): scroll to top when href="/" and hash already present (#32954)

When the URL already contains a hash (like `/#section`) and a `Link` with `href="/"` is clicked, the page should scroll to the top on the first click. Currently, it only happens with the second click (after `#section` has been removed from the URL).

Fixes #32931

## Bug

- [ ] Related issues linked using `fixes #number`
- [ ] Integration tests added
- [ ] Errors have helpful link attached, see `contributing.md`

## Feature

- [ ] Implements an existing feature request or RFC. Make sure the feature request has been accepted for implementation before opening a PR.
- [ ] Related issues linked using `fixes #number`
- [ ] Integration tests added
- [ ] Documentation added
- [ ] Telemetry added. In case of a feature if it's used or not.
- [ ] Errors have helpful link attached, see `contributing.md`

## Documentation / Examples

- [ ] Make sure the linting passes by running `yarn lint`
This commit is contained in:
Balázs Orbán 2022-01-03 03:57:02 +01:00 committed by GitHub
parent 381ca374e5
commit be1d308417
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 68 additions and 1 deletions

View file

@ -1615,7 +1615,7 @@ export default class Router implements BaseRouter {
} }
scrollToHash(as: string): void { scrollToHash(as: string): void {
const [, hash] = as.split('#') const [, hash = ''] = as.split('#')
// Scroll to top if the hash is just `#` with no value or `#top` // Scroll to top if the hash is just `#` with no value or `#top`
// To mirror browsers // To mirror browsers
if (hash === '' || hash === 'top') { if (hash === '' || hash === 'top') {

View file

@ -0,0 +1,16 @@
import Link from 'next/link'
export default function Page() {
return (
<div>
<div style={{ height: '100vh' }} />
<Link href="/">
<a id="top-link">top</a>
</Link>
<Link href="#section">
<a id="section-link">section link</a>
</Link>
<section id="section">section content</section>
</div>
)
}

View file

@ -0,0 +1,51 @@
/* eslint-env jest */
import { join } from 'path'
import webdriver from 'next-webdriver'
import {
findPort,
launchApp,
killApp,
nextStart,
nextBuild,
} from 'next-test-utils'
let app
let appPort
const appDir = join(__dirname, '../')
function runTests() {
it('scrolls to top when href="/" and url already contains a hash', async () => {
const browser = await webdriver(appPort, '/#section')
expect(await browser.eval(() => window.scrollY)).not.toBe(0)
await browser.elementByCss('#top-link').click()
expect(await browser.eval(() => window.scrollY)).toBe(0)
await browser.close()
})
}
describe('router.isReady', () => {
describe('dev mode', () => {
beforeAll(async () => {
appPort = await findPort()
app = await launchApp(appDir, appPort)
})
afterAll(async () => {
await killApp(app)
})
runTests()
})
describe('production mode', () => {
beforeAll(async () => {
await nextBuild(appDir)
appPort = await findPort()
app = await nextStart(appDir, appPort)
})
afterAll(() => killApp(app))
runTests()
})
})