Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/BootstrapBlazor/BootstrapBlazor.csproj
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk.Razor">

<PropertyGroup>
<Version>10.9.1-beta01</Version>
<Version>10.9.1-beta02</Version>
</PropertyGroup>

<ItemGroup>
Expand Down
27 changes: 27 additions & 0 deletions src/BootstrapBlazor/Components/TreeView/TreeView.razor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -908,12 +908,39 @@ private async Task OnCheckStateChanged(TreeViewItem<TItem> item, CheckboxState s
_ = InvokeVoidAsync("setParentState", Id, Interop, nameof(GetParentsState), Rows.IndexOf(item));
}

SyncSearchItemCheckedState(item);

if (OnTreeItemChecked != null)
{
await OnTreeItemChecked([.. GetCheckedItems()]);
}
}

private void SyncSearchItemCheckedState(TreeViewItem<TItem> item)
{
if (_searchItems == null)
{
return;
}

var sourceItem = _treeNodeStateCache.Find(Items, item.Value, out _);
if (sourceItem == null || ReferenceEquals(sourceItem, item))
{
return;
}

sourceItem.CheckedState = item.CheckedState;
_treeNodeStateCache.ToggleCheck(sourceItem);
if (AutoCheckChildren && sourceItem.CheckedState != CheckboxState.Indeterminate)
{
sourceItem.SetChildrenCheck(_treeNodeStateCache);
}
if (AutoCheckParent)
{
sourceItem.SetParentCheck(_treeNodeStateCache);
}
}

/// <summary>
/// <para lang="zh">清除所有选中的节点</para>
/// <para lang="en">Clear all selected nodes</para>
Expand Down
89 changes: 89 additions & 0 deletions test/UnitTest/Components/TreeViewTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1557,6 +1557,95 @@
Assert.Equal(items[1].Text, cut.Find(".active .tree-node-text").TextContent);
}

[Fact]
public async Task Search_CheckedItem_Ok()
{
var items = TreeFoo.GetTreeItems();
List<TreeViewItem<TreeFoo>>? checkedItems = null;
List<TreeViewItem<TreeFoo>> searchItems =
[
new(new TreeFoo { Id = items[0].Value.Id, Text = items[0].Value.Text }) { Text = items[0].Text }
];
var cut = Context.Render<TreeView<TreeFoo>>(pb =>
{
pb.Add(a => a.ShowSearch, true);
pb.Add(a => a.ShowCheckbox, true);
pb.Add(a => a.Items, items);
pb.Add(a => a.OnSearchAsync, _ => Task.FromResult<List<TreeViewItem<TreeFoo>>?>(searchItems));
pb.Add(a => a.OnTreeItemChecked, result =>
{
checkedItems = result;
return Task.CompletedTask;
});
});

var input = cut.FindComponent<BootstrapInput<string?>>();
await cut.InvokeAsync(() => input.Instance.OnEnterAsync!(items[0].Text));

var checkbox = cut.FindComponent<Checkbox<TreeViewItem<TreeFoo>>>();
await cut.InvokeAsync(checkbox.Instance.OnToggleClick);
Assert.Single(checkedItems!);
Assert.Equal(items[0].Value.Id, checkedItems[0].Value.Id);

Check warning on line 1588 in test/UnitTest/Components/TreeViewTest.cs

View workflow job for this annotation

GitHub Actions / run test

Dereference of a possibly null reference.
Assert.Equal(CheckboxState.Checked, items[0].CheckedState);

await cut.InvokeAsync(checkbox.Instance.OnToggleClick);
Assert.Empty(checkedItems!);
Assert.Equal(CheckboxState.UnChecked, items[0].CheckedState);

searchItems = [new(new TreeFoo { Id = "NotFound", Text = "NotFound" }) { Text = "NotFound" }];
await cut.InvokeAsync(() => input.Instance.OnEnterAsync!(string.Empty));
checkbox = cut.FindComponent<Checkbox<TreeViewItem<TreeFoo>>>();
await cut.InvokeAsync(checkbox.Instance.OnToggleClick);
Assert.Empty(checkedItems!);

searchItems = [items[0]];
await cut.InvokeAsync(() => input.Instance.OnEnterAsync!(string.Empty));
checkbox = cut.FindComponent<Checkbox<TreeViewItem<TreeFoo>>>();
await cut.InvokeAsync(checkbox.Instance.OnToggleClick);
Assert.Single(checkedItems!);
Assert.Same(items[0], checkedItems[0]);
}

[Fact]
public async Task Search_CheckedItem_Cascade_Ok()
{
var root = new TreeViewItem<TreeFoo>(new TreeFoo { Id = "Root", Text = "Root" }) { Text = "Root" };
var child = new TreeViewItem<TreeFoo>(new TreeFoo { Id = "Child", Text = "Child" }) { Text = "Child", Parent = root };
root.Items.Add(child);
List<TreeViewItem<TreeFoo>>? checkedItems = null;
var cut = Context.Render<TreeView<TreeFoo>>(pb =>
{
pb.Add(a => a.ShowSearch, true);
pb.Add(a => a.ShowCheckbox, true);
pb.Add(a => a.AutoCheckChildren, true);
pb.Add(a => a.AutoCheckParent, true);
pb.Add(a => a.Items, new List<TreeViewItem<TreeFoo>> { root });
pb.Add(a => a.OnSearchAsync, _ => Task.FromResult<List<TreeViewItem<TreeFoo>>?>(
[
new(new TreeFoo { Id = root.Value.Id, Text = root.Value.Text }) { Text = root.Text }
]));
pb.Add(a => a.OnTreeItemChecked, result =>
{
checkedItems = result;
return Task.CompletedTask;
});
});

var input = cut.FindComponent<BootstrapInput<string?>>();
await cut.InvokeAsync(() => input.Instance.OnEnterAsync!(root.Text));
var checkbox = cut.FindComponent<Checkbox<TreeViewItem<TreeFoo>>>();

await cut.InvokeAsync(() => checkbox.Instance.SetState(CheckboxState.Indeterminate));
Assert.Empty(checkedItems!);
Assert.Equal(CheckboxState.Indeterminate, root.CheckedState);
Assert.Equal(CheckboxState.UnChecked, child.CheckedState);

await cut.InvokeAsync(() => checkbox.Instance.SetState(CheckboxState.Checked));
Assert.Equal(2, checkedItems!.Count);
Assert.Equal(CheckboxState.Checked, root.CheckedState);
Assert.Equal(CheckboxState.Checked, child.CheckedState);
}

[Fact]
public async Task Search_NullBranches_Ok()
{
Expand All @@ -1566,7 +1655,7 @@
{
pb.Add(a => a.ShowSearch, true);
pb.Add(a => a.Items, items);
pb.Add(a => a.OnSearchAsync, _ => Task.FromResult(searchResult));

Check warning on line 1658 in test/UnitTest/Components/TreeViewTest.cs

View workflow job for this annotation

GitHub Actions / run test

Nullability of reference types in value of type 'Task<List<TreeViewItem<TreeFoo>>>' doesn't match target type 'Task<List<TreeViewItem<TreeFoo>>?>'.
});
var input = cut.FindComponent<BootstrapInput<string?>>();

Expand Down
Loading