feat(theme): NavRailSection data-nav-key for persistence
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
@* Components/NavRailSection.razor — CSS-only collapsible (no JS, works in static SSR).
|
@* Components/NavRailSection.razor — CSS-only collapsible (no JS, works in static SSR).
|
||||||
Apps that want cookie-persisted expand state keep their own interactive NavSection. *@
|
Apps that want cookie-persisted expand state keep their own interactive NavSection. *@
|
||||||
@namespace ZB.MOM.WW.Theme
|
@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>
|
<summary class="rail-eyebrow-toggle">@Title</summary>
|
||||||
<div class="rail-section-body">@ChildContent</div>
|
<div class="rail-section-body">@ChildContent</div>
|
||||||
</details>
|
</details>
|
||||||
@@ -18,8 +18,23 @@
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
[Parameter] public bool Expanded { get; set; } = true;
|
[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>
|
/// <summary>
|
||||||
/// Section content — typically <see cref="NavRailItem"/> children.
|
/// Section content — typically <see cref="NavRailItem"/> children.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Parameter] public RenderFragment? ChildContent { get; set; }
|
[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>"));
|
.AddChildContent("<a class='rail-link'>X</a>"));
|
||||||
Assert.False(cut.Find("details.rail-section").HasAttribute("open"));
|
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"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user