Skip to content

Commit

Permalink
Implement breadcrumbs fixes #138 (#148)
Browse files Browse the repository at this point in the history
  • Loading branch information
Mpdreamz authored Jan 8, 2025
1 parent 59eb87c commit d4f6654
Show file tree
Hide file tree
Showing 7 changed files with 120 additions and 23 deletions.
66 changes: 50 additions & 16 deletions src/Elastic.Markdown/IO/DocumentationFolder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,41 +6,75 @@ namespace Elastic.Markdown.IO;

public class DocumentationFolder
{
public MarkdownFile? Index { get; }
public MarkdownFile? Index { get; set; }

public List<MarkdownFile> FilesInOrder { get; } = new();
public List<DocumentationFolder> GroupsInOrder { get; } = new();
public List<MarkdownFile> FilesInOrder { get; }
public List<DocumentationFolder> GroupsInOrder { get; }

public required DocumentationFolder? Parent { get; init; }

private HashSet<MarkdownFile> OwnFiles { get; }

public int Level { get; }

public DocumentationFolder(IReadOnlyCollection<ITocItem> toc,
public DocumentationFolder(
IReadOnlyCollection<ITocItem> toc,
IDictionary<string, DocumentationFile> lookup,
IDictionary<string, DocumentationFile[]> folderLookup,
int level = 0,
MarkdownFile? index = null)
MarkdownFile? index = null
)
{
Level = level;
Index = index;
var foundIndex = ProcessTocItems(toc, lookup, folderLookup, level, out var groupsInOrder, out var filesInOrder);

GroupsInOrder = groupsInOrder;
FilesInOrder = filesInOrder;
Index = index ?? foundIndex;

if (Index is not null)
{
FilesInOrder = FilesInOrder.Except([Index]).ToList();
Index.Parent ??= this;
}

OwnFiles = [.. FilesInOrder];
}

private MarkdownFile? ProcessTocItems(
IReadOnlyCollection<ITocItem> toc,
IDictionary<string, DocumentationFile> lookup,
IDictionary<string, DocumentationFile[]> folderLookup,
int level,
out List<DocumentationFolder> groupsInOrder,
out List<MarkdownFile> filesInOrder
)
{
groupsInOrder = [];
filesInOrder = [];
MarkdownFile? index = null;
foreach (var tocItem in toc)
{
if (tocItem is TocFile file)
{
if (!lookup.TryGetValue(file.Path, out var d) || d is not MarkdownFile md)
continue;

md.Parent = this;

if (file.Children.Count > 0 && d is MarkdownFile virtualIndex)
{
var group = new DocumentationFolder(file.Children, lookup, folderLookup, level + 1, virtualIndex);
GroupsInOrder.Add(group);
var group = new DocumentationFolder(file.Children, lookup, folderLookup, level + 1, virtualIndex)
{
Parent = this
};
groupsInOrder.Add(group);
continue;
}

FilesInOrder.Add(md);
filesInOrder.Add(md);
if (file.Path.EndsWith("index.md") && d is MarkdownFile i)
Index ??= i;
index ??= i;
}
else if (tocItem is TocFolder folder)
{
Expand All @@ -53,15 +87,15 @@ public DocumentationFolder(IReadOnlyCollection<ITocItem> toc,
.ToArray();
}

var group = new DocumentationFolder(children, lookup, folderLookup, level + 1);
GroupsInOrder.Add(group);
var group = new DocumentationFolder(children, lookup, folderLookup, level + 1)
{
Parent = this
};
groupsInOrder.Add(group);
}
}

Index ??= FilesInOrder.FirstOrDefault();
if (Index != null)
FilesInOrder = FilesInOrder.Except(new[] { Index }).ToList();
OwnFiles = [.. FilesInOrder];
return index ?? filesInOrder.FirstOrDefault();
}

public bool HoldsCurrent(MarkdownFile current) =>
Expand Down
5 changes: 4 additions & 1 deletion src/Elastic.Markdown/IO/DocumentationSet.cs
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,10 @@ public DocumentationSet(BuildContext context)
.GroupBy(file => file.RelativeFolder)
.ToDictionary(g => g.Key, g => g.ToArray());

Tree = new DocumentationFolder(Configuration.TableOfContents, FlatMappedFiles, folderFiles);
Tree = new DocumentationFolder(Configuration.TableOfContents, FlatMappedFiles, folderFiles)
{
Parent = null
};
}

public MarkdownFile? GetMarkdownFile(IFileInfo sourceFile)
Expand Down
17 changes: 16 additions & 1 deletion src/Elastic.Markdown/IO/MarkdownFile.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,9 @@ public MarkdownFile(IFileInfo sourceFile, IDirectoryInfo rootPath, MarkdownParse
Collector = context.Collector;
}

public DiagnosticsCollector Collector { get; }
private DiagnosticsCollector Collector { get; }

public DocumentationFolder? Parent { get; set; }

public string? UrlPathPrefix { get; }
private MarkdownParser MarkdownParser { get; }
Expand All @@ -54,6 +56,19 @@ public string? NavigationTitle

private bool _instructionsParsed;

public MarkdownFile[] YieldParents()
{
var parents = new List<MarkdownFile>();
var parent = Parent;
do
{
if (parent is { Index: not null } && parent.Index != this)
parents.Add(parent.Index);
parent = parent?.Parent;
} while (parent != null);
return parents.ToArray();
}

public async Task<MarkdownDocument> MinimalParse(Cancel ctx)
{
var document = await MarkdownParser.MinimalParseAsync(SourceFile, ctx);
Expand Down
3 changes: 3 additions & 0 deletions src/Elastic.Markdown/Slices/HtmlWriter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ public async Task<string> RenderLayout(MarkdownFile markdown, Cancel ctx = defau
var html = markdown.CreateHtml(document);
await DocumentationSet.Tree.Resolve(ctx);
var navigationHtml = await RenderNavigation(markdown, ctx);

var previous = DocumentationSet;

var slice = Index.Create(new IndexViewModel
{
Title = markdown.Title ?? "[TITLE NOT SET]",
Expand Down
15 changes: 10 additions & 5 deletions src/Elastic.Markdown/Slices/_Layout.cshtml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
</div>
<div class="sy-page sy-container flex mx-auto">
@(new HtmlString(Model.NavigationHtml))
@*@(await RenderPartialAsync(Elastic.Markdown.Slices.Layout._TocTree.Create(Model)))*@
@(await RenderPartialAsync(_TableOfContents.Create(Model)))
<main class="sy-main w-full max-sm:max-w-full print:pt-6">
<div class="sy-breadcrumbs" role="navigation">
Expand All @@ -35,10 +34,16 @@
<span>/</span>
<meta itemprop="position" content="1">
</li>
<li itemprop="itemListElement" itemscope="" itemtype="https://schema.org/ListItem">
<strong itemprop="name">Elastic Docs v3</strong>
<meta itemprop="position" content="2">
</li>
@foreach (var item in Model.Parents.Reverse())
{
<li itemprop="itemListElement" itemscope="" itemtype="https://schema.org/ListItem">
<a itemprop="item" href="@item.Url">
<span itemprop="name">@item.NavigationTitle</span>
</a>
<span>/</span>
<meta itemprop="position" content="2">
</li>
}
</ol>
<div class="xl:hidden ml-1">
<button class="js-menu" aria-label="Show table of contents" type="button" aria-controls="rside" aria-expanded="false">
Expand Down
12 changes: 12 additions & 0 deletions src/Elastic.Markdown/Slices/_ViewModels.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,18 @@ public class LayoutViewModel
public required string NavigationHtml { get; set; }
public required string? UrlPathPrefix { get; set; }

private MarkdownFile[]? _parents;
public MarkdownFile[] Parents
{
get
{
if (_parents is not null)
return _parents;

_parents = [.. CurrentDocument.YieldParents()];
return _parents;
}
}

public string Static(string path)
{
Expand Down
25 changes: 25 additions & 0 deletions tests/Elastic.Markdown.Tests/SiteMap/BreadCrumbTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// Licensed to Elasticsearch B.V under one or more agreements.
// Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
// See the LICENSE file in the project root for more information

using Elastic.Markdown.IO;
using FluentAssertions;
using Xunit.Abstractions;

namespace Elastic.Markdown.Tests.SiteMap;

public class BreadCrumbTests(ITestOutputHelper output) : NavigationTestsBase(output)
{
[Fact]
public void ParsesATableOfContents()
{
var doc = Generator.DocumentationSet.Files.FirstOrDefault(f => f.RelativePath == "testing/nested/index.md") as MarkdownFile;

doc.Should().NotBeNull();

doc!.Parent.Should().NotBeNull();

doc.YieldParents().Should().HaveCount(2);

}
}

0 comments on commit d4f6654

Please sign in to comment.