feat(ui): add selection support to TreeView (R5)

This commit is contained in:
Joseph Doherty
2026-03-23 02:26:09 -04:00
parent 75648c0c76
commit da4f29f6ee
2 changed files with 108 additions and 16 deletions

View File

@@ -29,20 +29,39 @@ public class TreeViewTests : BunitContext
List<TestNode>? items = null,
RenderFragment? emptyContent = null,
int indentPx = 24,
Func<TestNode, bool>? initiallyExpanded = null)
Func<TestNode, bool>? initiallyExpanded = null,
bool selectable = false,
object? selectedKey = null,
Action<object?>? onSelectedKeyChanged = null,
string? selectedCssClass = null)
{
return Render<TreeView<TestNode>>(parameters => parameters
.Add(p => p.Items, items ?? SimpleRoots())
.Add(p => p.ChildrenSelector, n => n.Children)
.Add(p => p.HasChildrenSelector, n => n.Children.Count > 0)
.Add(p => p.KeySelector, n => n.Key)
.Add(p => p.NodeContent, node => builder =>
return Render<TreeView<TestNode>>(parameters =>
{
parameters
.Add(p => p.Items, items ?? SimpleRoots())
.Add(p => p.ChildrenSelector, n => n.Children)
.Add(p => p.HasChildrenSelector, n => n.Children.Count > 0)
.Add(p => p.KeySelector, n => n.Key)
.Add(p => p.NodeContent, node => builder =>
{
builder.AddMarkupContent(0, $"<span class=\"node-label\">{node.Label}</span>");
})
.Add(p => p.IndentPx, indentPx)
.Add(p => p.EmptyContent, emptyContent)
.Add(p => p.InitiallyExpanded, initiallyExpanded)
.Add(p => p.Selectable, selectable)
.Add(p => p.SelectedKey, selectedKey);
if (onSelectedKeyChanged != null)
{
builder.AddMarkupContent(0, $"<span class=\"node-label\">{node.Label}</span>");
})
.Add(p => p.IndentPx, indentPx)
.Add(p => p.EmptyContent, emptyContent)
.Add(p => p.InitiallyExpanded, initiallyExpanded));
parameters.Add(p => p.SelectedKeyChanged, onSelectedKeyChanged);
}
if (selectedCssClass != null)
{
parameters.Add(p => p.SelectedCssClass, selectedCssClass);
}
});
}
[Fact]
@@ -238,4 +257,64 @@ public class TreeViewTests : BunitContext
var alpha2xRow = rows[3];
Assert.Contains("padding-left: 60px", alpha2xRow.GetAttribute("style"));
}
[Fact]
public void Selection_Disabled_ClickDoesNotFireCallback()
{
object? selected = null;
var cut = RenderTreeView(selectable: false, onSelectedKeyChanged: k => selected = k);
cut.Find(".tv-content").Click();
Assert.Null(selected);
}
[Fact]
public void Selection_Enabled_ClickContentFiresCallback()
{
object? selected = null;
var cut = RenderTreeView(selectable: true, onSelectedKeyChanged: k => selected = k);
cut.Find(".tv-content").Click();
Assert.Equal("a", selected);
}
[Fact]
public void Selection_ClickToggle_DoesNotChangeSelection()
{
object? selected = null;
var cut = RenderTreeView(selectable: true, onSelectedKeyChanged: k => selected = k);
cut.Find(".tv-toggle").Click();
Assert.Null(selected);
}
[Fact]
public void Selection_SelectedNode_HasCssClass()
{
var cut = RenderTreeView(selectable: true, selectedKey: "a");
var alphaRow = cut.FindAll(".tv-row")[0];
Assert.Contains("bg-primary", alphaRow.GetAttribute("class"));
}
[Fact]
public void Selection_CustomCssClass_Applied()
{
var cut = RenderTreeView(selectable: true, selectedKey: "a", selectedCssClass: "my-highlight");
var alphaRow = cut.FindAll(".tv-row")[0];
Assert.Contains("my-highlight", alphaRow.GetAttribute("class"));
}
[Fact]
public void Selection_AriaSelected_SetOnSelectedNode()
{
var cut = RenderTreeView(selectable: true, selectedKey: "a");
var alphaLi = cut.FindAll("li[role='treeitem']")[0];
Assert.Equal("true", alphaLi.GetAttribute("aria-selected"));
}
}