diff --git a/src/BootstrapBlazor/BootstrapBlazor.csproj b/src/BootstrapBlazor/BootstrapBlazor.csproj index fdb43786d9d..9045861cc53 100644 --- a/src/BootstrapBlazor/BootstrapBlazor.csproj +++ b/src/BootstrapBlazor/BootstrapBlazor.csproj @@ -1,7 +1,7 @@  - 10.9.1-beta01 + 10.9.1-beta02 diff --git a/src/BootstrapBlazor/Components/TreeView/TreeView.razor.cs b/src/BootstrapBlazor/Components/TreeView/TreeView.razor.cs index d6a8d1785f7..01cca014889 100644 --- a/src/BootstrapBlazor/Components/TreeView/TreeView.razor.cs +++ b/src/BootstrapBlazor/Components/TreeView/TreeView.razor.cs @@ -908,12 +908,39 @@ private async Task OnCheckStateChanged(TreeViewItem item, CheckboxState s _ = InvokeVoidAsync("setParentState", Id, Interop, nameof(GetParentsState), Rows.IndexOf(item)); } + SyncSearchItemCheckedState(item); + if (OnTreeItemChecked != null) { await OnTreeItemChecked([.. GetCheckedItems()]); } } + private void SyncSearchItemCheckedState(TreeViewItem 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); + } + } + /// /// 清除所有选中的节点 /// Clear all selected nodes diff --git a/test/UnitTest/Components/TreeViewTest.cs b/test/UnitTest/Components/TreeViewTest.cs index f0e906fcb42..27d53e00013 100644 --- a/test/UnitTest/Components/TreeViewTest.cs +++ b/test/UnitTest/Components/TreeViewTest.cs @@ -1557,6 +1557,95 @@ public async Task Search_SelectedItem_Ok() Assert.Equal(items[1].Text, cut.Find(".active .tree-node-text").TextContent); } + [Fact] + public async Task Search_CheckedItem_Ok() + { + var items = TreeFoo.GetTreeItems(); + List>? checkedItems = null; + List> searchItems = + [ + new(new TreeFoo { Id = items[0].Value.Id, Text = items[0].Value.Text }) { Text = items[0].Text } + ]; + var cut = Context.Render>(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>?>(searchItems)); + pb.Add(a => a.OnTreeItemChecked, result => + { + checkedItems = result; + return Task.CompletedTask; + }); + }); + + var input = cut.FindComponent>(); + await cut.InvokeAsync(() => input.Instance.OnEnterAsync!(items[0].Text)); + + var checkbox = cut.FindComponent>>(); + await cut.InvokeAsync(checkbox.Instance.OnToggleClick); + Assert.Single(checkedItems!); + Assert.Equal(items[0].Value.Id, checkedItems[0].Value.Id); + 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>>(); + await cut.InvokeAsync(checkbox.Instance.OnToggleClick); + Assert.Empty(checkedItems!); + + searchItems = [items[0]]; + await cut.InvokeAsync(() => input.Instance.OnEnterAsync!(string.Empty)); + checkbox = cut.FindComponent>>(); + 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(new TreeFoo { Id = "Root", Text = "Root" }) { Text = "Root" }; + var child = new TreeViewItem(new TreeFoo { Id = "Child", Text = "Child" }) { Text = "Child", Parent = root }; + root.Items.Add(child); + List>? checkedItems = null; + var cut = Context.Render>(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> { root }); + pb.Add(a => a.OnSearchAsync, _ => Task.FromResult>?>( + [ + 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>(); + await cut.InvokeAsync(() => input.Instance.OnEnterAsync!(root.Text)); + var checkbox = cut.FindComponent>>(); + + 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() {