Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/Components/Web/src/PublicAPI.Unshipped.txt
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
#nullable enable
virtual Microsoft.AspNetCore.Components.Routing.NavLink.ShouldMatch(string! currentUriAbsolute) -> bool
9 changes: 7 additions & 2 deletions src/Components/Web/src/Routing/NavLink.cs
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,12 @@ private void OnLocationChanged(object? sender, LocationChangedEventArgs args)
}
}

private bool ShouldMatch(string currentUriAbsolute)
/// <summary>
/// Determines whether the current URI should match the link.
/// </summary>
/// <param name="currentUriAbsolute">The absolute URI of the current location.</param>
/// <returns>True if the link should be highlighted as active; otherwise, false.</returns>
protected virtual bool ShouldMatch(string currentUriAbsolute)
{
if (_hrefAbsolute == null)
{
Expand Down Expand Up @@ -199,7 +204,7 @@ private static bool IsStrictlyPrefixWithSeparator(string value, string prefix)

private static bool IsUnreservedCharacter(char c)
{
// Checks whether it is an unreserved character according to
// Checks whether it is an unreserved character according to
// https://datatracker.ietf.org/doc/html/rfc3986#section-2.3
// Those are characters that are allowed in a URI but do not have a reserved
// purpose (e.g. they do not separate the components of the URI)
Expand Down
28 changes: 28 additions & 0 deletions src/Components/test/E2ETest/Tests/RoutingTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -383,6 +383,34 @@ public void CanFollowLinkDefinedInOpenShadowRoot()
AssertHighlightedLinks("Other", "Other with base-relative URL (matches all)");
}

[Fact]
public void CanOverrideNavLinkToIgnoreFragment()
{
SetUrlViaPushState("/layout-overridden/for-hash");

var app = Browser.MountTestComponent<TestRouter>();
app.FindElement(By.LinkText("Override layout with hash, no trailing slash")).Click();
Browser.Equal("This is the page with overridden layout.", () => app.FindElement(By.Id("test-info")).Text);
AssertHighlightedLinks(
"Override layout (matches all)",
"Override layout, no trailing slash (matches all)",
"Override layout with hash, no trailing slash");
}

[Fact]
public void CanOverrideNavLinkToIgnoreQuery()
{
SetUrlViaPushState("/layout-overridden");

var app = Browser.MountTestComponent<TestRouter>();
app.FindElement(By.LinkText("Override layout with query, no trailing slash")).Click();
Browser.Equal("This is the page with overridden layout.", () => app.FindElement(By.Id("test-info")).Text);
AssertHighlightedLinks(
"Override layout (matches all)",
"Override layout, no trailing slash (matches all)",
"Override layout with query, no trailing slash");
}

[Fact]
public void CanGoBackFromNotAComponent()
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
@page "/layout-overridden"
@page "/layout-overridden/for-hash"
@layout RouterTestLayoutNavLinksOverridden
<div id="test-info">This is the page with overridden layout.</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
@using Microsoft.AspNetCore.Components.Routing
<style type="text/css">
a.active {
background-color: yellow;
font-weight: bold;
}
</style>
<ul>
<li><NavLinkIgnoreQueryAndFragmentString href="/subdir/layout-overridden/" Match=NavLinkMatch.All>Override layout (matches all)</NavLinkIgnoreQueryAndFragmentString></li>
<li><NavLinkIgnoreQueryAndFragmentString href="/subdir/layout-overridden" Match=NavLinkMatch.All>Override layout, no trailing slash (matches all)</NavLinkIgnoreQueryAndFragmentString></li>
<li><NavLinkIgnoreQueryAndFragmentString href="/subdir/layout-overridden/?abc=123">Override layout with query</NavLinkIgnoreQueryAndFragmentString></li>
<li><NavLinkIgnoreQueryAndFragmentString href="/subdir/layout-overridden?abc=123">Override layout with query, no trailing slash</NavLinkIgnoreQueryAndFragmentString></li>
<li><NavLinkIgnoreQueryAndFragmentString href="/subdir/layout-overridden/#blah">Override layout with hash</NavLinkIgnoreQueryAndFragmentString></li>
<li><NavLinkIgnoreQueryAndFragmentString href="/subdir/layout-overridden#blah">Override layout with hash, no trailing slash</NavLinkIgnoreQueryAndFragmentString></li>
<li><NavLinkIgnoreQueryAndFragmentString href="/subdir/layout-overridden/Default.html">Override layout with extension</NavLinkIgnoreQueryAndFragmentString></li>
<li><NavLinkIgnoreQueryAndFragmentString href="/subdir/layout-overridden/Other">Override Other</NavLinkIgnoreQueryAndFragmentString></li>
<li><NavLinkIgnoreQueryAndFragmentString href="/subdir/Other" Match=NavLinkMatch.All>Override Other with base-relative URL (matches all)</NavLinkIgnoreQueryAndFragmentString></li>
</ul>
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
@using Microsoft.AspNetCore.Components.Routing;
@inherits NavLink

@{
base.BuildRenderTree(__builder);
}

@code{

protected override bool ShouldMatch(string currentUriAbsolute)
{
if (base.ShouldMatch(currentUriAbsolute))
{
return true;
}

var originalMatch = base.Match;
base.Match = NavLinkMatch.All;
string uriWithoutQueryAndFragment = GetUriIgnoreQueryAndFragment(currentUriAbsolute);
var shouldMatch = base.ShouldMatch(uriWithoutQueryAndFragment);
base.Match = originalMatch;
return shouldMatch;
}

private string GetUriIgnoreQueryAndFragment(string uri) =>
new Uri(uri).GetLeftPart(UriPartial.Path);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
@using Microsoft.AspNetCore.Components
@inherits LayoutComponentBase

@Body

<BasicTestApp.RouterTest.LinksOverridden />
Loading