Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/Elastic.ApiExplorer/ApiViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using Elastic.Documentation.Configuration;
using Elastic.Documentation.Configuration.Assembler;
using Elastic.Documentation.Configuration.Builder;
using Elastic.Documentation.Extensions;
using Elastic.Documentation.Site;
using Elastic.Documentation.Site.FileProviders;
using Elastic.Documentation.Site.Navigation;
Expand All @@ -29,6 +30,7 @@ public HtmlString RenderMarkdown(string? markdown) =>
public GlobalLayoutViewModel CreateGlobalLayoutModel() =>
new()
{
DocsBuilderVersion = ShortId.Create(BuildContext.Version),
DocSetName = "Api Explorer",
Description = "",
CurrentNavigationItem = CurrentNavigationItem,
Expand Down
3 changes: 3 additions & 0 deletions src/Elastic.Documentation.Configuration/BuildContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// See the LICENSE file in the project root for more information

using System.IO.Abstractions;
using System.Reflection;
using Elastic.Documentation.Configuration.Assembler;
using Elastic.Documentation.Configuration.Builder;
using Elastic.Documentation.Diagnostics;
Expand All @@ -11,6 +12,8 @@ namespace Elastic.Documentation.Configuration;

public record BuildContext : IDocumentationContext
{
public static string Version { get; } = Assembly.GetExecutingAssembly().GetCustomAttributes<AssemblyInformationalVersionAttribute>()
.FirstOrDefault()?.InformationalVersion ?? "0.0.0";
public IFileSystem ReadFileSystem { get; }
public IFileSystem WriteFileSystem { get; }

Expand Down
24 changes: 24 additions & 0 deletions src/Elastic.Documentation.Site/Assets/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,3 +91,27 @@ document.body.addEventListener('htmx:responseError', function (event) {
window.location.assign(event.detail.pathInfo.requestPath)
}
})

// We add a query string to the get request to make sure the requested page is up to date
const docsBuilderVersion = $('body').dataset.docsBuilderVersion
document.body.addEventListener('htmx:configRequest', function (event) {
if (event.detail.verb === 'get') {
event.detail.parameters['v'] = docsBuilderVersion
}
})

// Here we need to strip the v parameter from the URL so
// that the browser doesn't show the v parameter in the address bar
document.body.addEventListener('htmx:beforeHistoryUpdate', function (event) {
const params = new URLSearchParams(
event.detail.history.path.split('?')[1] ?? ''
)
params.delete('v')
const pathWithoutQueryString = event.detail.history.path.split('?')[0]
if (params.size === 0) {
event.detail.history.path = pathWithoutQueryString
} else {
event.detail.history.path =
pathWithoutQueryString + '?' + params.toString()
}
})
74 changes: 1 addition & 73 deletions src/Elastic.Documentation.Site/Htmx.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,91 +9,19 @@

namespace Elastic.Documentation.Site;

public static class UrlHelper
{
private static readonly KeyValuePair<string, string?>[] VersionParameters = [new("v", Htmx.VersionHash)];

public static string AddVersionParameters(string uri) => AddQueryString(uri, VersionParameters);

/// <summary>
/// Append the given query keys and values to the URI.
/// </summary>
/// <param name="uri">The base URI.</param>
/// <param name="queryString">A collection of name value query pairs to append.</param>
/// <returns>The combined result.</returns>
/// <exception cref="ArgumentNullException"><paramref name="uri"/> is <c>null</c>.</exception>
/// <exception cref="ArgumentNullException"><paramref name="queryString"/> is <c>null</c>.</exception>
public static string AddQueryString(
string uri,
IEnumerable<KeyValuePair<string, string?>> queryString)
{
ArgumentNullException.ThrowIfNull(uri);
ArgumentNullException.ThrowIfNull(queryString);

var anchorIndex = uri.IndexOf('#');
var uriToBeAppended = uri.AsSpan();
var anchorText = ReadOnlySpan<char>.Empty;
// If there is an anchor, then the query string must be inserted before its first occurrence.
if (anchorIndex != -1)
{
anchorText = uriToBeAppended.Slice(anchorIndex);
uriToBeAppended = uriToBeAppended.Slice(0, anchorIndex);
}

var queryIndex = uriToBeAppended.IndexOf('?');
var hasQuery = queryIndex != -1;

var sb = new StringBuilder();
_ = sb.Append(uriToBeAppended);
foreach (var parameter in queryString)
{
if (parameter.Value == null)
continue;

_ = sb.Append(hasQuery ? '&' : '?')
.Append(UrlEncoder.Default.Encode(parameter.Key))
.Append('=')
.Append(UrlEncoder.Default.Encode(parameter.Value));
hasQuery = true;
}

_ = sb.Append(anchorText);
return sb.ToString();
}
}

public static class Htmx
{
private static readonly string Version =
Assembly.GetExecutingAssembly().GetCustomAttributes<AssemblyInformationalVersionAttribute>()
.FirstOrDefault()?.InformationalVersion ?? "0.0.0";

public static readonly string VersionHash = ShortId.Create(Version);

public static string GetHxSelectOob(bool hasSameTopLevelGroup) => hasSameTopLevelGroup ? "#content-container,#toc-nav" : "#main-container";
public const string Preload = "mousedown";
public const string HxSwap = "none";
public const string HxPushUrl = "true";
public const string HxIndicator = "#htmx-indicator";

public static string GetHxAttributes(
string targetUrl,
bool hasSameTopLevelGroup = false,
string? preload = Preload,
string? hxSwapOob = null,
string? hxSwap = HxSwap,
string? hxPushUrl = HxPushUrl,
string? hxIndicator = HxIndicator
string? hxSwapOob = null
)
{
var hxGetUrl = UrlHelper.AddVersionParameters(targetUrl);

var attributes = new StringBuilder();
_ = attributes.Append($" hx-get={hxGetUrl}");
_ = attributes.Append($" hx-select-oob={hxSwapOob ?? GetHxSelectOob(hasSameTopLevelGroup)}");
_ = attributes.Append($" hx-swap={hxSwap}");
_ = attributes.Append($" hx-push-url={hxPushUrl}");
_ = attributes.Append($" hx-indicator={hxIndicator}");
_ = attributes.Append($" preload={preload}");
return attributes.ToString();
}
Expand Down
3 changes: 0 additions & 3 deletions src/Elastic.Documentation.Site/Layout/_SecondaryNav.cshtml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
<a
href="@Model.Link("/release-notes/")"
@Htmx.GetHxAttributes(
targetUrl: Model.Link("/release-notes/"),
hxSwapOob: "#main-container,#secondary-nav"
)
>
Expand All @@ -30,7 +29,6 @@
<a
href="@Model.Link("/troubleshoot/")"
@Htmx.GetHxAttributes(
targetUrl: Model.Link("/troubleshoot/"),
hxSwapOob: "#main-container,#secondary-nav"
)
>
Expand All @@ -41,7 +39,6 @@
<a
href="@Model.Link("/reference/")"
@Htmx.GetHxAttributes(
Model.Link("/reference/"),
hxSwapOob: "#main-container,#secondary-nav"
)
>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
@using Elastic.Documentation.Site.Navigation
@inherits RazorSlice<Elastic.Documentation.Site.Navigation.NavigationViewModel>
<div class="pb-20 font-body" hx-boost="true" hx-swap="none" hx-push-url="true" hx-indicator="#htmx-indicator">
<div class="pb-20 font-body">
@{
var currentTopLevelItem = Model.TopLevelItems.FirstOrDefault(i => i.Id == Model.Tree.Id) ?? Model.Tree;
}
Expand Down
5 changes: 4 additions & 1 deletion src/Elastic.Documentation.Site/_GlobalLayout.cshtml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,10 @@
<body
class="group/body text-ink has-[#primary-nav-hamburger:checked]:overflow-hidden"
hx-ext="preload, head-support"
data-root-path="@Model.Link("/")">
data-docs-builder-version="@Model.DocsBuilderVersion"
data-root-path="@Model.Link("/")"
hx-boost="true" hx-swap="none" hx-push-url="true" hx-indicator="#htmx-indicator"
>
@if (Model.GoogleTagManager.Enabled)
{
<noscript><iframe src="https://www.googletagmanager.com/ns.html?id=@(Model.GoogleTagManager.Id)@(new HtmlString(Model.GoogleTagManager.QueryString()))"
Expand Down
1 change: 1 addition & 0 deletions src/Elastic.Documentation.Site/_ViewModels.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ public static class GlobalSections

public record GlobalLayoutViewModel
{
public required string DocsBuilderVersion { get; init; }
public required string DocSetName { get; init; }
public string Title { get; set; } = "Elastic Documentation";
public required string Description { get; init; }
Expand Down
4 changes: 2 additions & 2 deletions src/Elastic.Markdown/Layout/_Breadcrumbs.cshtml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
<a
itemprop="item"
href="@firstCrumb.Url"
@Htmx.GetHxAttributes(firstCrumb.Url, Model.CurrentNavigationItem?.NavigationRoot.Id == firstCrumb.NavigationRoot.Id)
@Htmx.GetHxAttributes( Model.CurrentNavigationItem?.NavigationRoot.Id == firstCrumb.NavigationRoot.Id)
>
<span itemprop="name" class="hover:text-black">@firstCrumb.NavigationTitle</span>
</a>
Expand All @@ -41,7 +41,7 @@
<a
itemprop="item"
href="@item.Url"
@Htmx.GetHxAttributes(item.Url, Model.CurrentNavigationItem?.NavigationRoot.Id == item.NavigationRoot.Id)
@Htmx.GetHxAttributes(Model.CurrentNavigationItem?.NavigationRoot.Id == item.NavigationRoot.Id)
>
<span itemprop="name" class="hover:text-black">@item.NavigationTitle</span>
</a>
Expand Down
4 changes: 2 additions & 2 deletions src/Elastic.Markdown/Layout/_PrevNextNav.cshtml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
{
<a
href="@Model.Previous.Url"
@Htmx.GetHxAttributes(Model.Previous.Url, Model.CurrentNavigationItem?.NavigationRoot.Id == Model.Previous.NavigationRoot.Id)
@Htmx.GetHxAttributes(Model.CurrentNavigationItem?.NavigationRoot.Id == Model.Previous.NavigationRoot.Id)
class="flex h-full items-center text-ink-light hover:black border-1 border-grey-20 hover:border-grey-80 rounded-lg p-6 shadow-md"
>
<svg class="size-6 mr-2" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
Expand All @@ -24,7 +24,7 @@
{
<a
href="@Model.Next.Url"
@Htmx.GetHxAttributes(Model.Next.Url, Model.CurrentNavigationItem?.NavigationRoot.Id == Model.Next.NavigationRoot.Id)
@Htmx.GetHxAttributes(Model.CurrentNavigationItem?.NavigationRoot.Id == Model.Next.NavigationRoot.Id)
class="flex h-full items-center justify-end text-ink-light hover:black border-1 border-grey-20 hover:border-grey-80 rounded-lg p-6 shadow-md text-right"
>
<div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,6 @@ protected override void Write(HtmlRenderer renderer, LinkInline link)
}

var url = link.GetDynamicUrl?.Invoke() ?? link.Url;
var hxGetUrl = url;
if (hxGetUrl is not null)
hxGetUrl = UrlHelper.AddVersionParameters(hxGetUrl);

var isCrossLink = (link.GetData("isCrossLink") as bool?) == true;
var isHttpLink = url?.StartsWith("http") ?? false;
Expand All @@ -45,13 +42,7 @@ protected override void Write(HtmlRenderer renderer, LinkInline link)
var currentRootNavigation = link.GetData(nameof(MarkdownFile.NavigationRoot)) as INodeNavigationItem<INavigationModel, INavigationItem>;
var targetRootNavigation = link.GetData($"Target{nameof(MarkdownFile.NavigationRoot)}") as INodeNavigationItem<INavigationModel, INavigationItem>;
var hasSameTopLevelGroup = !isCrossLink && (currentRootNavigation?.Id == targetRootNavigation?.Id);
_ = renderer.Write(" hx-get=\"");
_ = renderer.WriteEscapeUrl(hxGetUrl);
_ = renderer.Write('"');
_ = renderer.Write($" hx-select-oob=\"{Htmx.GetHxSelectOob(hasSameTopLevelGroup)}\"");
_ = renderer.Write($" hx-swap=\"{Htmx.HxSwap}\"");
_ = renderer.Write($" hx-push-url=\"{Htmx.HxPushUrl}\"");
_ = renderer.Write($" hx-indicator=\"{Htmx.HxIndicator}\"");
_ = renderer.Write($" preload=\"{Htmx.Preload}\"");
}
if (isHttpLink && !isCrossLink)
Expand Down
3 changes: 3 additions & 0 deletions src/Elastic.Markdown/Page/Index.cshtml
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
@using System.Text.Json
@using Elastic.Documentation.Configuration
@using Elastic.Documentation.Extensions
@using Elastic.Markdown
@using Elastic.Markdown.Myst.Components
@using Markdig
Expand All @@ -8,6 +10,7 @@
@functions {
public MarkdownLayoutViewModel LayoutModel => new()
{
DocsBuilderVersion = ShortId.Create(BuildContext.Version),
Layout = Model.CurrentDocument.YamlFrontMatter?.Layout,
RenderHamburgerIcon = Model.CurrentDocument.YamlFrontMatter?.Layout != MarkdownPageLayout.LandingPage,
DocSetName = Model.DocSetName,
Expand Down
7 changes: 1 addition & 6 deletions tests/Elastic.Markdown.Tests/Inline/AnchorLinkTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -74,14 +74,9 @@ [Sub Requirements](testing/req.md#sub-requirements)
public void GeneratesHtml() =>
// language=html
Html.ShouldContainHtml(
"""<p><a href="/docs/testing/req#sub-requirements" hx-get="/docs/testing/req#sub-requirements" hx-select-oob="#content-container,#toc-nav" hx-swap="none" hx-push-url="true" hx-indicator="#htmx-indicator" preload="mousedown">Sub Requirements</a></p>"""
"""<p><a href="/docs/testing/req#sub-requirements" hx-select-oob="#content-container,#toc-nav" preload="mousedown">Sub Requirements</a></p>"""
);

[Fact]
public void HxGetContainsVersionAnchor() =>
// language=html
Html.Should().MatchRegex("""hx-get="/docs/testing/req\?v=(.+?)#sub-requirements""");

[Fact]
public void HasNoErrors() => Collector.Diagnostics.Should().HaveCount(0);
}
Expand Down
Loading