Skip to content

Commit d4f6654

Browse files
authored
Implement breadcrumbs fixes #138 (#148)
1 parent 59eb87c commit d4f6654

File tree

7 files changed

+120
-23
lines changed

7 files changed

+120
-23
lines changed

src/Elastic.Markdown/IO/DocumentationFolder.cs

Lines changed: 50 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -6,41 +6,75 @@ namespace Elastic.Markdown.IO;
66

77
public class DocumentationFolder
88
{
9-
public MarkdownFile? Index { get; }
9+
public MarkdownFile? Index { get; set; }
1010

11-
public List<MarkdownFile> FilesInOrder { get; } = new();
12-
public List<DocumentationFolder> GroupsInOrder { get; } = new();
11+
public List<MarkdownFile> FilesInOrder { get; }
12+
public List<DocumentationFolder> GroupsInOrder { get; }
13+
14+
public required DocumentationFolder? Parent { get; init; }
1315

1416
private HashSet<MarkdownFile> OwnFiles { get; }
1517

1618
public int Level { get; }
1719

18-
public DocumentationFolder(IReadOnlyCollection<ITocItem> toc,
20+
public DocumentationFolder(
21+
IReadOnlyCollection<ITocItem> toc,
1922
IDictionary<string, DocumentationFile> lookup,
2023
IDictionary<string, DocumentationFile[]> folderLookup,
2124
int level = 0,
22-
MarkdownFile? index = null)
25+
MarkdownFile? index = null
26+
)
2327
{
2428
Level = level;
25-
Index = index;
29+
var foundIndex = ProcessTocItems(toc, lookup, folderLookup, level, out var groupsInOrder, out var filesInOrder);
30+
31+
GroupsInOrder = groupsInOrder;
32+
FilesInOrder = filesInOrder;
33+
Index = index ?? foundIndex;
34+
35+
if (Index is not null)
36+
{
37+
FilesInOrder = FilesInOrder.Except([Index]).ToList();
38+
Index.Parent ??= this;
39+
}
40+
41+
OwnFiles = [.. FilesInOrder];
42+
}
2643

44+
private MarkdownFile? ProcessTocItems(
45+
IReadOnlyCollection<ITocItem> toc,
46+
IDictionary<string, DocumentationFile> lookup,
47+
IDictionary<string, DocumentationFile[]> folderLookup,
48+
int level,
49+
out List<DocumentationFolder> groupsInOrder,
50+
out List<MarkdownFile> filesInOrder
51+
)
52+
{
53+
groupsInOrder = [];
54+
filesInOrder = [];
55+
MarkdownFile? index = null;
2756
foreach (var tocItem in toc)
2857
{
2958
if (tocItem is TocFile file)
3059
{
3160
if (!lookup.TryGetValue(file.Path, out var d) || d is not MarkdownFile md)
3261
continue;
3362

63+
md.Parent = this;
64+
3465
if (file.Children.Count > 0 && d is MarkdownFile virtualIndex)
3566
{
36-
var group = new DocumentationFolder(file.Children, lookup, folderLookup, level + 1, virtualIndex);
37-
GroupsInOrder.Add(group);
67+
var group = new DocumentationFolder(file.Children, lookup, folderLookup, level + 1, virtualIndex)
68+
{
69+
Parent = this
70+
};
71+
groupsInOrder.Add(group);
3872
continue;
3973
}
4074

41-
FilesInOrder.Add(md);
75+
filesInOrder.Add(md);
4276
if (file.Path.EndsWith("index.md") && d is MarkdownFile i)
43-
Index ??= i;
77+
index ??= i;
4478
}
4579
else if (tocItem is TocFolder folder)
4680
{
@@ -53,15 +87,15 @@ public DocumentationFolder(IReadOnlyCollection<ITocItem> toc,
5387
.ToArray();
5488
}
5589

56-
var group = new DocumentationFolder(children, lookup, folderLookup, level + 1);
57-
GroupsInOrder.Add(group);
90+
var group = new DocumentationFolder(children, lookup, folderLookup, level + 1)
91+
{
92+
Parent = this
93+
};
94+
groupsInOrder.Add(group);
5895
}
5996
}
6097

61-
Index ??= FilesInOrder.FirstOrDefault();
62-
if (Index != null)
63-
FilesInOrder = FilesInOrder.Except(new[] { Index }).ToList();
64-
OwnFiles = [.. FilesInOrder];
98+
return index ?? filesInOrder.FirstOrDefault();
6599
}
66100

67101
public bool HoldsCurrent(MarkdownFile current) =>

src/Elastic.Markdown/IO/DocumentationSet.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,10 @@ public DocumentationSet(BuildContext context)
5656
.GroupBy(file => file.RelativeFolder)
5757
.ToDictionary(g => g.Key, g => g.ToArray());
5858

59-
Tree = new DocumentationFolder(Configuration.TableOfContents, FlatMappedFiles, folderFiles);
59+
Tree = new DocumentationFolder(Configuration.TableOfContents, FlatMappedFiles, folderFiles)
60+
{
61+
Parent = null
62+
};
6063
}
6164

6265
public MarkdownFile? GetMarkdownFile(IFileInfo sourceFile)

src/Elastic.Markdown/IO/MarkdownFile.cs

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,9 @@ public MarkdownFile(IFileInfo sourceFile, IDirectoryInfo rootPath, MarkdownParse
2929
Collector = context.Collector;
3030
}
3131

32-
public DiagnosticsCollector Collector { get; }
32+
private DiagnosticsCollector Collector { get; }
33+
34+
public DocumentationFolder? Parent { get; set; }
3335

3436
public string? UrlPathPrefix { get; }
3537
private MarkdownParser MarkdownParser { get; }
@@ -54,6 +56,19 @@ public string? NavigationTitle
5456

5557
private bool _instructionsParsed;
5658

59+
public MarkdownFile[] YieldParents()
60+
{
61+
var parents = new List<MarkdownFile>();
62+
var parent = Parent;
63+
do
64+
{
65+
if (parent is { Index: not null } && parent.Index != this)
66+
parents.Add(parent.Index);
67+
parent = parent?.Parent;
68+
} while (parent != null);
69+
return parents.ToArray();
70+
}
71+
5772
public async Task<MarkdownDocument> MinimalParse(Cancel ctx)
5873
{
5974
var document = await MarkdownParser.MinimalParseAsync(SourceFile, ctx);

src/Elastic.Markdown/Slices/HtmlWriter.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,9 @@ public async Task<string> RenderLayout(MarkdownFile markdown, Cancel ctx = defau
4444
var html = markdown.CreateHtml(document);
4545
await DocumentationSet.Tree.Resolve(ctx);
4646
var navigationHtml = await RenderNavigation(markdown, ctx);
47+
48+
var previous = DocumentationSet;
49+
4750
var slice = Index.Create(new IndexViewModel
4851
{
4952
Title = markdown.Title ?? "[TITLE NOT SET]",

src/Elastic.Markdown/Slices/_Layout.cshtml

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717
</div>
1818
<div class="sy-page sy-container flex mx-auto">
1919
@(new HtmlString(Model.NavigationHtml))
20-
@*@(await RenderPartialAsync(Elastic.Markdown.Slices.Layout._TocTree.Create(Model)))*@
2120
@(await RenderPartialAsync(_TableOfContents.Create(Model)))
2221
<main class="sy-main w-full max-sm:max-w-full print:pt-6">
2322
<div class="sy-breadcrumbs" role="navigation">
@@ -35,10 +34,16 @@
3534
<span>/</span>
3635
<meta itemprop="position" content="1">
3736
</li>
38-
<li itemprop="itemListElement" itemscope="" itemtype="https://schema.org/ListItem">
39-
<strong itemprop="name">Elastic Docs v3</strong>
40-
<meta itemprop="position" content="2">
41-
</li>
37+
@foreach (var item in Model.Parents.Reverse())
38+
{
39+
<li itemprop="itemListElement" itemscope="" itemtype="https://schema.org/ListItem">
40+
<a itemprop="item" href="@item.Url">
41+
<span itemprop="name">@item.NavigationTitle</span>
42+
</a>
43+
<span>/</span>
44+
<meta itemprop="position" content="2">
45+
</li>
46+
}
4247
</ol>
4348
<div class="xl:hidden ml-1">
4449
<button class="js-menu" aria-label="Show table of contents" type="button" aria-controls="rside" aria-expanded="false">

src/Elastic.Markdown/Slices/_ViewModels.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,18 @@ public class LayoutViewModel
2727
public required string NavigationHtml { get; set; }
2828
public required string? UrlPathPrefix { get; set; }
2929

30+
private MarkdownFile[]? _parents;
31+
public MarkdownFile[] Parents
32+
{
33+
get
34+
{
35+
if (_parents is not null)
36+
return _parents;
37+
38+
_parents = [.. CurrentDocument.YieldParents()];
39+
return _parents;
40+
}
41+
}
3042

3143
public string Static(string path)
3244
{
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// Licensed to Elasticsearch B.V under one or more agreements.
2+
// Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3+
// See the LICENSE file in the project root for more information
4+
5+
using Elastic.Markdown.IO;
6+
using FluentAssertions;
7+
using Xunit.Abstractions;
8+
9+
namespace Elastic.Markdown.Tests.SiteMap;
10+
11+
public class BreadCrumbTests(ITestOutputHelper output) : NavigationTestsBase(output)
12+
{
13+
[Fact]
14+
public void ParsesATableOfContents()
15+
{
16+
var doc = Generator.DocumentationSet.Files.FirstOrDefault(f => f.RelativePath == "testing/nested/index.md") as MarkdownFile;
17+
18+
doc.Should().NotBeNull();
19+
20+
doc!.Parent.Should().NotBeNull();
21+
22+
doc.YieldParents().Should().HaveCount(2);
23+
24+
}
25+
}

0 commit comments

Comments
 (0)