feat(theme): NavRailSection data-nav-key for persistence

This commit is contained in:
Joseph Doherty
2026-06-03 02:53:15 -04:00
parent 7e11f9aac8
commit edd49765d6
2 changed files with 34 additions and 1 deletions
@@ -1,7 +1,7 @@
@* Components/NavRailSection.razor — CSS-only collapsible (no JS, works in static SSR).
Apps that want cookie-persisted expand state keep their own interactive NavSection. *@
@namespace ZB.MOM.WW.Theme
<details class="rail-section" open="@Expanded">
<details class="rail-section" open="@Expanded" data-nav-key="@ResolvedKey">
<summary class="rail-eyebrow-toggle">@Title</summary>
<div class="rail-section-body">@ChildContent</div>
</details>
@@ -18,8 +18,23 @@
/// </summary>
[Parameter] public bool Expanded { get; set; } = true;
/// <summary>
/// Stable identifier used to persist this section's open/closed state in
/// localStorage (via the kit's nav-state.js). Defaults to a slug of <see cref="Title"/>.
/// </summary>
[Parameter] public string? Key { get; set; }
/// <summary>
/// Section content — typically <see cref="NavRailItem"/> children.
/// </summary>
[Parameter] public RenderFragment? ChildContent { get; set; }
private string ResolvedKey => string.IsNullOrWhiteSpace(Key) ? Slug(Title) : Key!;
private static string Slug(string s)
{
var chars = s.Trim().ToLowerInvariant()
.Select(c => char.IsLetterOrDigit(c) ? c : '-').ToArray();
return string.Join('-', new string(chars).Split('-', StringSplitOptions.RemoveEmptyEntries));
}
}
@@ -55,4 +55,22 @@ public class NavRailTests : TestContext
.AddChildContent("<a class='rail-link'>X</a>"));
Assert.False(cut.Find("details.rail-section").HasAttribute("open"));
}
[Fact]
public void NavRailSection_emits_data_nav_key_slug_from_title_by_default()
{
var cut = RenderComponent<NavRailSection>(p => p
.Add(x => x.Title, "Site Calls")
.AddChildContent("<a class='rail-link'>X</a>"));
Assert.Equal("site-calls", cut.Find("details.rail-section").GetAttribute("data-nav-key"));
}
[Fact]
public void NavRailSection_emits_explicit_key_when_supplied()
{
var cut = RenderComponent<NavRailSection>(p => p
.Add(x => x.Title, "Navigation").Add(x => x.Key, "nav")
.AddChildContent("<a class='rail-link'>X</a>"));
Assert.Equal("nav", cut.Find("details.rail-section").GetAttribute("data-nav-key"));
}
}