diff --git a/src/BootstrapBlazor/Components/TreeView/TreeView.razor.cs b/src/BootstrapBlazor/Components/TreeView/TreeView.razor.cs index 291a1337a35..27c72a17120 100644 --- a/src/BootstrapBlazor/Components/TreeView/TreeView.razor.cs +++ b/src/BootstrapBlazor/Components/TreeView/TreeView.razor.cs @@ -427,7 +427,7 @@ protected override async Task OnParametersSetAsync() if (_init == false) { _activeItem ??= Items.FirstOrDefaultActiveItem(); - _activeItem?.SetParentExpand, TItem>(true); + ExpandParentNodes(_activeItem); _init = true; _scrollIntoView = true; } @@ -762,11 +762,27 @@ private Task OnClickResetSearch() public void SetActiveItem(TreeViewItem? item) { _activeItem = item; - _activeItem?.SetParentExpand, TItem>(true); + ExpandParentNodes(_activeItem); + _rows = null; _scrollIntoView = true; StateHasChanged(); } + /// + /// 向上级联展开祖先节点并同步节点缓存状态 防止缓存中的收缩状态覆盖此处的展开状态 + /// Cascade expand ancestor nodes upwards and synchronize the node cache state to prevent the collapsed state in the cache from overriding the expanded state set here + /// + /// + private void ExpandParentNodes(TreeViewItem? item) + { + var parent = item?.Parent; + while (parent is TreeViewItem p) + { + _treeNodeStateCache.ExpandNode(p); + parent = p.Parent; + } + } + /// /// Set the 数据 source method for /// Set the data source method for diff --git a/src/BootstrapBlazor/Misc/ExpandableNodeCache.cs b/src/BootstrapBlazor/Misc/ExpandableNodeCache.cs index 1f4d3d4dc9b..1338834842a 100644 --- a/src/BootstrapBlazor/Misc/ExpandableNodeCache.cs +++ b/src/BootstrapBlazor/Misc/ExpandableNodeCache.cs @@ -79,6 +79,18 @@ public virtual async Task ToggleNodeAsync(TNode node, Func + /// 展开指定节点并同步缓存状态 + /// Expand the specified node and synchronize the cache state + /// + /// + public void ExpandNode(IExpandableNode node) + { + node.IsExpand = true; + ExpandedNodeCache.Add(node.Value); + CollapsedNodeCache.Remove(node.Value); + } + /// /// 检查当前节点是否展开 /// Check whether current node is expanded @@ -89,14 +101,16 @@ public async Task CheckExpandAsync(TNode node, Func cut.Instance.SetActiveItem(activeItem)); } + [Fact] + public async Task SetActiveItem_ExpandCollapsedParent() + { + // https://github.com/dotnetcore/BootstrapBlazor/issues/8185 + // 手动收缩父节点后再设置激活子节点 祖先节点应重新展开 + var items = TreeFoo.GetTreeItems(); + var cut = Context.Render>(pb => + { + pb.Add(a => a.Items, items); + }); + + // 初始激活节点为 Sub Menu Three 祖先节点 Navigation two 处于展开状态 + // 手动收缩 Navigation two 节点 + await cut.InvokeAsync(() => cut.FindAll(".node-icon.visible")[0].Click()); + Assert.DoesNotContain("Sub menu 1", cut.Markup); + + // 设置激活节点为 Navigation two 的子节点 Sub menu 1 祖先节点应重新展开 + await cut.InvokeAsync(() => cut.Instance.SetActiveItem(items[1].Items[0].Value)); + Assert.Contains("Sub menu 1", cut.Markup); + + // 模拟父组件重新渲染 缓存中的收缩状态不应覆盖程序设置的展开状态 + cut.Render(); + var node = cut.Find(".active .tree-node-text"); + Assert.Equal("Sub menu 1", node.TextContent); + + // 再次渲染 确认展开状态稳定 不出现展开收缩循环切换 + cut.Render(); + node = cut.Find(".active .tree-node-text"); + Assert.Equal("Sub menu 1", node.TextContent); + + // 再次手动收缩激活节点的祖先节点后重新渲染 保持用户手动设置的收缩状态 + await cut.InvokeAsync(() => cut.FindAll(".node-icon.visible")[0].Click()); + cut.Render(); + Assert.DoesNotContain("Sub menu 1", cut.Markup); + } + [Fact] public void AppendNode_Ok() { diff --git a/test/UnitTest/Dynamic/ChangeDetectionCleanTaskTest.cs b/test/UnitTest/Dynamic/ChangeDetectionCleanTaskTest.cs index 808122cb7f0..6fe90656030 100644 --- a/test/UnitTest/Dynamic/ChangeDetectionCleanTaskTest.cs +++ b/test/UnitTest/Dynamic/ChangeDetectionCleanTaskTest.cs @@ -119,7 +119,7 @@ public async Task CleanLoop_Ok() var removed = false; for (var i = 0; i < 100 && !removed; i++) { - await Task.Delay(50); + await Task.Delay(50, Xunit.TestContext.Current.CancellationToken); removed = !cache.ContainsKey(dynamicType); } Assert.True(removed);