diff --git a/src/Elastic.Documentation.Navigation/Isolated/Node/FolderNavigation.cs b/src/Elastic.Documentation.Navigation/Isolated/Node/FolderNavigation.cs index f2a9991fd6..ec8f39576d 100644 --- a/src/Elastic.Documentation.Navigation/Isolated/Node/FolderNavigation.cs +++ b/src/Elastic.Documentation.Navigation/Isolated/Node/FolderNavigation.cs @@ -32,7 +32,7 @@ public class FolderNavigation( public INodeNavigationItem? Parent { get; set; } = parent; /// - public bool Hidden { get; } + public bool Hidden { get; private set; } /// public int NavigationIndex { get; set; } @@ -50,6 +50,7 @@ internal void SetNavigationItems(IReadOnlyCollection navigation { var indexNavigation = navigationItems.QueryIndex(this, $"{FolderPath}/index.md", out navigationItems); Index = indexNavigation; + Hidden = Index.Hidden; // Include NavigationRoot.Id to ensure uniqueness across docsets in assembler builds // (URLs alone aren't unique until path prefixes are applied by SiteNavigation) Id = ShortId.Create(NavigationRoot.Id, Index.Url); diff --git a/src/Elastic.Documentation.Navigation/Isolated/Node/TableOfContentsNavigation.cs b/src/Elastic.Documentation.Navigation/Isolated/Node/TableOfContentsNavigation.cs index 0613689846..c77426e546 100644 --- a/src/Elastic.Documentation.Navigation/Isolated/Node/TableOfContentsNavigation.cs +++ b/src/Elastic.Documentation.Navigation/Isolated/Node/TableOfContentsNavigation.cs @@ -77,7 +77,7 @@ INavigationHomeProvider homeProvider public INavigationHomeProvider HomeProvider { get; set; } /// - public bool Hidden { get; } + public bool Hidden { get; private set; } /// public int NavigationIndex { get; set; } @@ -104,6 +104,7 @@ internal void SetNavigationItems(IReadOnlyCollection navigation { var indexNavigation = navigationItems.QueryIndex(this, $"{ParentPath}/index.md", out navigationItems); Index = indexNavigation; + Hidden = Index.Hidden; // Include NavigationRoot.Id to ensure uniqueness across docsets in assembler builds Id = ShortId.Create(NavigationRoot.Id, Index.Url); NavigationItems = navigationItems; diff --git a/src/Elastic.Documentation.Navigation/NavigationItemExtensions.cs b/src/Elastic.Documentation.Navigation/NavigationItemExtensions.cs index dbdd327796..898ad5c454 100644 --- a/src/Elastic.Documentation.Navigation/NavigationItemExtensions.cs +++ b/src/Elastic.Documentation.Navigation/NavigationItemExtensions.cs @@ -16,16 +16,21 @@ this IReadOnlyCollection items, INodeNavigationItem LookupIndex() + ILeafNavigationItem? LookupIndex(bool preferVisible) { foreach (var item in items) { + if (preferVisible && item.Hidden) + continue; + // Check for the exact type match if (item is ILeafNavigationItem leaf) return leaf; @@ -35,6 +40,9 @@ ILeafNavigationItem LookupIndex() return nodeItem.Index; } + if (preferVisible) + return null; + // If no index is found, throw an exception throw new InvalidOperationException($"No index found for navigation node '{node.GetType().Name}' at path '{fallbackPath}'"); } diff --git a/tests/Navigation.Tests/Isolation/DynamicUrlTests.cs b/tests/Navigation.Tests/Isolation/DynamicUrlTests.cs index 20a206e40a..8399b5b636 100644 --- a/tests/Navigation.Tests/Isolation/DynamicUrlTests.cs +++ b/tests/Navigation.Tests/Isolation/DynamicUrlTests.cs @@ -113,6 +113,58 @@ public void FolderWithoutIndexUsesFirstChildUrl() folder!.Url.Should().Be("/guides/getting-started"); } + [Fact] + public void FolderWithoutIndexUsesFirstVisibleChildUrlWhenHiddenChildComesFirst() + { + // language=yaml + var yaml = """ + project: 'test-project' + toc: + - folder: guides + children: + - hidden: autopilot.md + - file: getting-started.md + """; + + var fileSystem = new MockFileSystem(); + fileSystem.AddDirectory("/docs"); + var context = CreateContext(); + var docSet = DocumentationSetFile.LoadAndResolve(context.Collector, yaml, fileSystem.NewDirInfo("docs")); + + var navigation = new DocumentationSetNavigation(docSet, context, GenericDocumentationFileFactory.Instance); + var folder = navigation.NavigationItems.First().Should().BeOfType>().Subject; + + folder.Hidden.Should().BeFalse(); + folder.NavigationTitle.Should().Be("getting-started"); + folder.Url.Should().Be("/guides/getting-started"); + folder.NavigationItems.Should().ContainSingle().Which.Hidden.Should().BeTrue(); + } + + [Fact] + public void FolderWithoutIndexAndOnlyHiddenChildrenIsHidden() + { + // language=yaml + var yaml = """ + project: 'test-project' + toc: + - folder: guides + children: + - hidden: autopilot.md + """; + + var fileSystem = new MockFileSystem(); + fileSystem.AddDirectory("/docs"); + var context = CreateContext(); + var docSet = DocumentationSetFile.LoadAndResolve(context.Collector, yaml, fileSystem.NewDirInfo("docs")); + + var navigation = new DocumentationSetNavigation(docSet, context, GenericDocumentationFileFactory.Instance); + var folder = navigation.NavigationItems.First().Should().BeOfType>().Subject; + + folder.Hidden.Should().BeTrue(); + folder.Index.Hidden.Should().BeTrue(); + folder.NavigationItems.Should().BeEmpty(); + } + [Fact] public void FolderWithNestedChildren() {