parent
8b7fcf1f76
commit
54da777b7c
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
|
@ -9,6 +9,7 @@ interface BrowserRendererRegistry {
|
||||||
[browserRendererId: number]: BrowserRenderer;
|
[browserRendererId: number]: BrowserRenderer;
|
||||||
}
|
}
|
||||||
const browserRenderers: BrowserRendererRegistry = {};
|
const browserRenderers: BrowserRendererRegistry = {};
|
||||||
|
let shouldResetScrollAfterNextBatch = false;
|
||||||
|
|
||||||
export function attachRootComponentToLogicalElement(browserRendererId: number, logicalElement: LogicalElement, componentId: number): void {
|
export function attachRootComponentToLogicalElement(browserRendererId: number, logicalElement: LogicalElement, componentId: number): void {
|
||||||
|
|
||||||
|
|
@ -67,4 +68,20 @@ export function renderBatch(browserRendererId: number, batch: RenderBatch): void
|
||||||
const eventHandlerId = batch.disposedEventHandlerIdsEntry(disposedEventHandlerIdsValues, i);
|
const eventHandlerId = batch.disposedEventHandlerIdsEntry(disposedEventHandlerIdsValues, i);
|
||||||
browserRenderer.disposeEventHandler(eventHandlerId);
|
browserRenderer.disposeEventHandler(eventHandlerId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
resetScrollIfNeeded();
|
||||||
|
}
|
||||||
|
|
||||||
|
export function resetScrollAfterNextBatch() {
|
||||||
|
shouldResetScrollAfterNextBatch = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function resetScrollIfNeeded() {
|
||||||
|
if (shouldResetScrollAfterNextBatch) {
|
||||||
|
shouldResetScrollAfterNextBatch = false;
|
||||||
|
|
||||||
|
// This assumes the scroller is on the window itself. There isn't a general way to know
|
||||||
|
// if some other element is playing the role of the primary scroll region.
|
||||||
|
window.scrollTo && window.scrollTo(0, 0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import '@dotnet/jsinterop';
|
import '@dotnet/jsinterop';
|
||||||
|
import { resetScrollAfterNextBatch } from '../Rendering/Renderer';
|
||||||
|
|
||||||
let hasRegisteredNavigationInterception = false;
|
let hasRegisteredNavigationInterception = false;
|
||||||
let hasRegisteredNavigationEventListeners = false;
|
let hasRegisteredNavigationEventListeners = false;
|
||||||
|
|
@ -81,6 +82,13 @@ export function navigateTo(uri: string, forceLoad: boolean) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function performInternalNavigation(absoluteInternalHref: string, interceptedLink: boolean) {
|
function performInternalNavigation(absoluteInternalHref: string, interceptedLink: boolean) {
|
||||||
|
// Since this was *not* triggered by a back/forward gesture (that goes through a different
|
||||||
|
// code path starting with a popstate event), we don't want to preserve the current scroll
|
||||||
|
// position, so reset it.
|
||||||
|
// To avoid ugly flickering effects, we don't want to change the scroll position until the
|
||||||
|
// we render the new page. As a best approximation, wait until the next batch.
|
||||||
|
resetScrollAfterNextBatch();
|
||||||
|
|
||||||
history.pushState(null, /* ignored title */ '', absoluteInternalHref);
|
history.pushState(null, /* ignored title */ '', absoluteInternalHref);
|
||||||
notifyLocationChanged(interceptedLink);
|
notifyLocationChanged(interceptedLink);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -418,6 +418,40 @@ namespace Microsoft.AspNetCore.Components.E2ETest.Tests
|
||||||
Assert.Equal("Oops, that component wasn't found!", app.FindElement(By.Id("test-info")).Text);
|
Assert.Equal("Oops, that component wasn't found!", app.FindElement(By.Id("test-info")).Text);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ResetsScrollPositionWhenPerformingInternalNavigation_LinkClick()
|
||||||
|
{
|
||||||
|
SetUrlViaPushState("/LongPage1");
|
||||||
|
var app = MountTestComponent<TestRouter>();
|
||||||
|
Browser.Equal("This is a long page you can scroll.", () => app.FindElement(By.Id("test-info")).Text);
|
||||||
|
BrowserScrollY = 500;
|
||||||
|
Browser.True(() => BrowserScrollY > 300); // Exact position doesn't matter
|
||||||
|
|
||||||
|
app.FindElement(By.LinkText("Long page 2")).Click();
|
||||||
|
Browser.Equal("This is another long page you can scroll.", () => app.FindElement(By.Id("test-info")).Text);
|
||||||
|
Browser.Equal(0, () => BrowserScrollY);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ResetsScrollPositionWhenPerformingInternalNavigation_ProgrammaticNavigation()
|
||||||
|
{
|
||||||
|
SetUrlViaPushState("/LongPage1");
|
||||||
|
var app = MountTestComponent<TestRouter>();
|
||||||
|
Browser.Equal("This is a long page you can scroll.", () => app.FindElement(By.Id("test-info")).Text);
|
||||||
|
BrowserScrollY = 500;
|
||||||
|
Browser.True(() => BrowserScrollY > 300); // Exact position doesn't matter
|
||||||
|
|
||||||
|
app.FindElement(By.Id("go-to-longpage2")).Click();
|
||||||
|
Browser.Equal("This is another long page you can scroll.", () => app.FindElement(By.Id("test-info")).Text);
|
||||||
|
Browser.Equal(0, () => BrowserScrollY);
|
||||||
|
}
|
||||||
|
|
||||||
|
private long BrowserScrollY
|
||||||
|
{
|
||||||
|
get => (long)((IJavaScriptExecutor)Browser).ExecuteScript("return window.scrollY");
|
||||||
|
set => ((IJavaScriptExecutor)Browser).ExecuteScript($"window.scrollTo(0, {value})");
|
||||||
|
}
|
||||||
|
|
||||||
private string SetUrlViaPushState(string relativeUri)
|
private string SetUrlViaPushState(string relativeUri)
|
||||||
{
|
{
|
||||||
var pathBaseWithoutHash = ServerPathBase.Split('#')[0];
|
var pathBaseWithoutHash = ServerPathBase.Split('#')[0];
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,8 @@
|
||||||
<li><NavLink href="/subdir/Other#blah">Other with hash</NavLink></li>
|
<li><NavLink href="/subdir/Other#blah">Other with hash</NavLink></li>
|
||||||
<li><NavLink href="/subdir/WithParameters/Name/Abc">With parameters</NavLink></li>
|
<li><NavLink href="/subdir/WithParameters/Name/Abc">With parameters</NavLink></li>
|
||||||
<li><NavLink href="/subdir/WithParameters/Name/Abc/LastName/McDef">With more parameters</NavLink></li>
|
<li><NavLink href="/subdir/WithParameters/Name/Abc/LastName/McDef">With more parameters</NavLink></li>
|
||||||
|
<li><NavLink href="/subdir/LongPage1">Long page 1</NavLink></li>
|
||||||
|
<li><NavLink href="/subdir/LongPage2">Long page 2</NavLink></li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<button id="do-navigation" @onclick=@(x => uriHelper.NavigateTo("Other"))>
|
<button id="do-navigation" @onclick=@(x => uriHelper.NavigateTo("Other"))>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
@page "/LongPage1"
|
||||||
|
@inject IUriHelper UriHelper
|
||||||
|
<div id="test-info">This is a long page you can scroll.</div>
|
||||||
|
|
||||||
|
<div style="border: 2px dashed red; margin: 1rem; padding: 1rem; height: 1500px;">
|
||||||
|
Scroll past me to find the links
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button id="go-to-longpage2" @onclick="@(() => UriHelper.NavigateTo("LongPage2"))">
|
||||||
|
Navigate programmatically to long page 2
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<Links />
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
@page "/LongPage2"
|
||||||
|
<div id="test-info">This is another long page you can scroll.</div>
|
||||||
|
|
||||||
|
<div style="border: 2px dashed blue; margin: 1rem; padding: 1rem; height: 1500px;">
|
||||||
|
Scroll past me to find the links
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Links />
|
||||||
Loading…
Reference in New Issue