feat(ui): add sessionStorage persistence for TreeView expansion state (R11)

This commit is contained in:
Joseph Doherty
2026-03-23 02:28:54 -04:00
parent da4f29f6ee
commit d3a6ed5f68
4 changed files with 125 additions and 3 deletions

View File

@@ -33,7 +33,8 @@ public class TreeViewTests : BunitContext
bool selectable = false,
object? selectedKey = null,
Action<object?>? onSelectedKeyChanged = null,
string? selectedCssClass = null)
string? selectedCssClass = null,
string? storageKey = null)
{
return Render<TreeView<TestNode>>(parameters =>
{
@@ -61,6 +62,11 @@ public class TreeViewTests : BunitContext
{
parameters.Add(p => p.SelectedCssClass, selectedCssClass);
}
if (storageKey != null)
{
parameters.Add(p => p.StorageKey, storageKey);
}
});
}
@@ -317,4 +323,64 @@ public class TreeViewTests : BunitContext
var alphaLi = cut.FindAll("li[role='treeitem']")[0];
Assert.Equal("true", alphaLi.GetAttribute("aria-selected"));
}
[Fact]
public void SessionStorage_NullKey_NoJsInteropCalls()
{
var cut = RenderTreeView();
// Expand Alpha
cut.Find(".tv-toggle").Click();
// No JS interop calls should have been made
Assert.Empty(JSInterop.Invocations);
}
[Fact]
public void SessionStorage_Set_ExpandWritesToStorage()
{
JSInterop.Setup<string?>("treeviewStorage.load", _ => true).SetResult(null);
JSInterop.SetupVoid("treeviewStorage.save", _ => true);
var cut = RenderTreeView(storageKey: "test-tree");
// Expand Alpha
cut.Find(".tv-toggle").Click();
// Verify save was called
var saveInvocations = JSInterop.Invocations
.Where(i => i.Identifier == "treeviewStorage.save")
.ToList();
Assert.Single(saveInvocations);
}
[Fact]
public void SessionStorage_RestoresExpandedOnMount()
{
JSInterop.Setup<string?>("treeviewStorage.load", _ => true).SetResult("[\"a\"]");
JSInterop.SetupVoid("treeviewStorage.save", _ => true);
var cut = RenderTreeView(storageKey: "test-tree");
// Alpha's children should be visible because "a" was restored from storage
var labels = cut.FindAll(".node-label");
Assert.Contains(labels, l => l.TextContent == "Alpha-1");
Assert.Contains(labels, l => l.TextContent == "Alpha-2");
}
[Fact]
public void SessionStorage_TakesPrecedenceOverInitiallyExpanded()
{
// Storage returns empty array — meaning user explicitly collapsed everything
JSInterop.Setup<string?>("treeviewStorage.load", _ => true).SetResult("[]");
JSInterop.SetupVoid("treeviewStorage.save", _ => true);
var cut = RenderTreeView(
storageKey: "test-tree",
initiallyExpanded: n => n.Key == "a");
// Alpha should NOT be expanded — storage (empty) wins over InitiallyExpanded
var labels = cut.FindAll(".node-label");
Assert.DoesNotContain(labels, l => l.TextContent == "Alpha-1");
}
}