test(ui): add external filtering tests for TreeView (R8)
This commit is contained in:
@@ -104,6 +104,12 @@ else
|
|||||||
ApplyInitialExpansion(_items);
|
ApplyInitialExpansion(_items);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Clear selection if the selected key no longer exists in the current items tree
|
||||||
|
if (Selectable && SelectedKey != null && _items is not null && !KeyExistsInTree(_items, SelectedKey))
|
||||||
|
{
|
||||||
|
_ = SelectedKeyChanged.InvokeAsync(null);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override async Task OnAfterRenderAsync(bool firstRender)
|
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||||
@@ -133,6 +139,20 @@ else
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private bool KeyExistsInTree(IReadOnlyList<TItem> items, object key)
|
||||||
|
{
|
||||||
|
foreach (var item in items)
|
||||||
|
{
|
||||||
|
if (key.Equals(KeySelector(item)))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
var children = ChildrenSelector(item);
|
||||||
|
if (children is { Count: > 0 } && KeyExistsInTree(children, key))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
private void ApplyInitialExpansion(IReadOnlyList<TItem> items)
|
private void ApplyInitialExpansion(IReadOnlyList<TItem> items)
|
||||||
{
|
{
|
||||||
foreach (var item in items)
|
foreach (var item in items)
|
||||||
|
|||||||
@@ -464,6 +464,119 @@ public class TreeViewTests : BunitContext
|
|||||||
Assert.Equal(2, labels.Count);
|
Assert.Equal(2, labels.Count);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ── External filtering tests (R8) ──────────────────────────────────
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Filtering_ReducedItems_HidesRemovedRoots()
|
||||||
|
{
|
||||||
|
var fullItems = SimpleRoots();
|
||||||
|
var cut = RenderTreeView(items: fullItems);
|
||||||
|
|
||||||
|
// Both roots visible
|
||||||
|
var labels = cut.FindAll(".node-label");
|
||||||
|
Assert.Equal(2, labels.Count);
|
||||||
|
|
||||||
|
// Re-render with only Alpha (Beta removed)
|
||||||
|
var alphaOnly = new List<TestNode> { fullItems[0] };
|
||||||
|
cut.Render(parameters =>
|
||||||
|
{
|
||||||
|
parameters
|
||||||
|
.Add(p => p.Items, alphaOnly)
|
||||||
|
.Add(p => p.ChildrenSelector, (Func<TestNode, IReadOnlyList<TestNode>>)(n => n.Children))
|
||||||
|
.Add(p => p.HasChildrenSelector, (Func<TestNode, bool>)(n => n.Children.Count > 0))
|
||||||
|
.Add(p => p.KeySelector, (Func<TestNode, object>)(n => n.Key))
|
||||||
|
.Add(p => p.NodeContent, (RenderFragment<TestNode>)(node => builder =>
|
||||||
|
{
|
||||||
|
builder.AddMarkupContent(0, $"<span class=\"node-label\">{node.Label}</span>");
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
|
||||||
|
labels = cut.FindAll(".node-label");
|
||||||
|
Assert.Single(labels);
|
||||||
|
Assert.Equal("Alpha", labels[0].TextContent);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Filtering_ExpansionStatePreserved()
|
||||||
|
{
|
||||||
|
var fullItems = SimpleRoots();
|
||||||
|
var cut = RenderTreeView(items: fullItems);
|
||||||
|
|
||||||
|
// Expand Alpha
|
||||||
|
cut.Find(".tv-toggle").Click();
|
||||||
|
Assert.Contains(cut.FindAll(".node-label"), l => l.TextContent == "Alpha-1");
|
||||||
|
|
||||||
|
// Re-render with only Alpha
|
||||||
|
var alphaOnly = new List<TestNode> { fullItems[0] };
|
||||||
|
cut.Render(parameters =>
|
||||||
|
{
|
||||||
|
parameters
|
||||||
|
.Add(p => p.Items, alphaOnly)
|
||||||
|
.Add(p => p.ChildrenSelector, (Func<TestNode, IReadOnlyList<TestNode>>)(n => n.Children))
|
||||||
|
.Add(p => p.HasChildrenSelector, (Func<TestNode, bool>)(n => n.Children.Count > 0))
|
||||||
|
.Add(p => p.KeySelector, (Func<TestNode, object>)(n => n.Key))
|
||||||
|
.Add(p => p.NodeContent, (RenderFragment<TestNode>)(node => builder =>
|
||||||
|
{
|
||||||
|
builder.AddMarkupContent(0, $"<span class=\"node-label\">{node.Label}</span>");
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
|
||||||
|
// Alpha-1 still visible (expansion state preserved)
|
||||||
|
Assert.Contains(cut.FindAll(".node-label"), l => l.TextContent == "Alpha-1");
|
||||||
|
|
||||||
|
// Re-render with full list again
|
||||||
|
cut.Render(parameters =>
|
||||||
|
{
|
||||||
|
parameters
|
||||||
|
.Add(p => p.Items, fullItems)
|
||||||
|
.Add(p => p.ChildrenSelector, (Func<TestNode, IReadOnlyList<TestNode>>)(n => n.Children))
|
||||||
|
.Add(p => p.HasChildrenSelector, (Func<TestNode, bool>)(n => n.Children.Count > 0))
|
||||||
|
.Add(p => p.KeySelector, (Func<TestNode, object>)(n => n.Key))
|
||||||
|
.Add(p => p.NodeContent, (RenderFragment<TestNode>)(node => builder =>
|
||||||
|
{
|
||||||
|
builder.AddMarkupContent(0, $"<span class=\"node-label\">{node.Label}</span>");
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
|
||||||
|
// Alpha-1 still visible after restoration
|
||||||
|
Assert.Contains(cut.FindAll(".node-label"), l => l.TextContent == "Alpha-1");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Filtering_SelectionCleared_WhenNodeDisappears()
|
||||||
|
{
|
||||||
|
var fullItems = SimpleRoots();
|
||||||
|
object? lastSelected = "b"; // track the last value passed to callback
|
||||||
|
var cut = RenderTreeView(
|
||||||
|
items: fullItems,
|
||||||
|
selectable: true,
|
||||||
|
selectedKey: "b",
|
||||||
|
onSelectedKeyChanged: k => lastSelected = k);
|
||||||
|
|
||||||
|
// Re-render with only Alpha (Beta disappears)
|
||||||
|
var alphaOnly = new List<TestNode> { fullItems[0] };
|
||||||
|
cut.Render(parameters =>
|
||||||
|
{
|
||||||
|
parameters
|
||||||
|
.Add(p => p.Items, alphaOnly)
|
||||||
|
.Add(p => p.ChildrenSelector, (Func<TestNode, IReadOnlyList<TestNode>>)(n => n.Children))
|
||||||
|
.Add(p => p.HasChildrenSelector, (Func<TestNode, bool>)(n => n.Children.Count > 0))
|
||||||
|
.Add(p => p.KeySelector, (Func<TestNode, object>)(n => n.Key))
|
||||||
|
.Add(p => p.NodeContent, (RenderFragment<TestNode>)(node => builder =>
|
||||||
|
{
|
||||||
|
builder.AddMarkupContent(0, $"<span class=\"node-label\">{node.Label}</span>");
|
||||||
|
}))
|
||||||
|
.Add(p => p.Selectable, true)
|
||||||
|
.Add(p => p.SelectedKey, (object?)"b")
|
||||||
|
.Add(p => p.SelectedKeyChanged, (Action<object?>)(k => lastSelected = k));
|
||||||
|
});
|
||||||
|
|
||||||
|
// SelectedKeyChanged should have been called with null
|
||||||
|
Assert.Null(lastSelected);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Context menu tests ──────────────────────────────────────────────
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void ContextMenu_Null_NoMenuRendered()
|
public void ContextMenu_Null_NoMenuRendered()
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user