Skip to content
Open
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
548541b
Add deeplink anchors
theletterf Aug 22, 2025
126e90f
Add anchors
theletterf Aug 22, 2025
9a92523
Edit docs
theletterf Aug 22, 2025
fa2ea6b
Format file
theletterf Aug 22, 2025
26c8fa5
Merge branch 'main' into add-deeplink-anchors-dropdown
theletterf Aug 26, 2025
bda669a
Instant and autogenerate anchors
theletterf Aug 27, 2025
035b3b5
Prettify
theletterf Aug 27, 2025
5a0d5f6
Merge branch 'main' into add-deeplink-anchors-dropdown
theletterf Aug 27, 2025
e5dfcf9
Remove test file
theletterf Aug 27, 2025
d597bec
Merge branch 'add-deeplink-anchors-dropdown' of github.com:elastic/do…
theletterf Aug 27, 2025
0fd16ed
Address peer edits
theletterf Aug 27, 2025
1008fd9
Merge branch 'main' into add-deeplink-anchors-dropdown
theletterf Aug 27, 2025
8a8418b
Update src/Elastic.Markdown/Myst/ParserContext.cs
theletterf Aug 27, 2025
b9bdaf9
Add tests
theletterf Aug 27, 2025
0c3ab5c
Merge branch 'add-deeplink-anchors-dropdown' of github.com:elastic/do…
theletterf Aug 27, 2025
29607ec
Prettify
theletterf Aug 27, 2025
520ade3
Merge branch 'main' into add-deeplink-anchors-dropdown
theletterf Aug 27, 2025
08e8136
Simplify dupe detection
theletterf Oct 2, 2025
f03502f
Merge branch 'main' into add-deeplink-anchors-dropdown
theletterf Oct 2, 2025
6568772
Docs
theletterf Oct 2, 2025
60d2b6a
Reconcile changes from main
theletterf Oct 2, 2025
e925652
Add hints
theletterf Oct 2, 2025
dbffa7d
Fix line numbers in hints
theletterf Oct 2, 2025
f078aea
Merge remote-tracking branch 'origin/main' into add-deeplink-anchors-…
theletterf Oct 6, 2025
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
41 changes: 41 additions & 0 deletions docs/syntax/dropdowns.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,3 +50,44 @@ Dropdown content
::::

:::::

## Deeplinking

Dropdowns support deeplinking through anchor links. When you navigate to a URL with a hash that points to a dropdown or content within a dropdown, the dropdown will automatically open. When you manually open a dropdown that has a name/anchor, the URL will automatically update to reflect the current state.

:::::{tab-set}

::::{tab-item} Output

:::{dropdown} Deeplink Example
:name: deeplink-example

This dropdown can be opened by navigating to `#deeplink-example`.

When you open this dropdown manually by clicking the title, the URL will automatically update to show `#deeplink-example`.

#### Nested Content [#nested-content]

You can also link directly to content within dropdowns. This content has the anchor `#nested-content`.

:::

::::

::::{tab-item} Markdown
```markdown
:::{dropdown} Deeplink Example
:name: deeplink-example

This dropdown can be opened by navigating to `#deeplink-example`.

When you open this dropdown manually by clicking the title, the URL will automatically update to show `#deeplink-example`.

#### Nested Content [#nested-content]

You can also link directly to content within dropdowns. This content has the anchor `#nested-content`.

:::
::::

:::::
7 changes: 7 additions & 0 deletions docs/testing/nested/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,10 @@ The files in this directory are used for testing purposes. Do not edit these fil
## Injecting a {{x}} is supported in headers.

This should show up in the file's table of contents too.

:::{dropdown} Dropdown Title
:name: dropdown-title

Text.

:::
4 changes: 2 additions & 2 deletions src/Elastic.Documentation.Site/Assets/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { initCopyButton } from './copybutton'
import { initHighlight } from './hljs'
import { initImageCarousel } from './image-carousel'
import './markdown/applies-to'
import { openDetailsWithAnchor } from './open-details-with-anchor'
import { initOpenDetailsWithAnchor } from './open-details-with-anchor'
import { initNav } from './pages-nav'
import { initSmoothScroll } from './smooth-scroll'
import { initTabs } from './tabs'
Expand Down Expand Up @@ -31,7 +31,7 @@ document.addEventListener('htmx:load', function (event) {
initNav()
}
initSmoothScroll()
openDetailsWithAnchor()
initOpenDetailsWithAnchor()
initImageCarousel()

const urlParams = new URLSearchParams(window.location.search)
Expand Down
63 changes: 53 additions & 10 deletions src/Elastic.Documentation.Site/Assets/open-details-with-anchor.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,66 @@
import { UAParser } from 'ua-parser-js'

const { getBrowser } = new UAParser()

// This is a fix for anchors in details elements in non-Chrome browsers.
// Opens details elements (dropdowns) when navigating to an anchor link within them
// This enables deeplinking to collapsed dropdown content
export function openDetailsWithAnchor() {
if (window.location.hash) {
const target = document.querySelector(window.location.hash)
if (target) {
const closestDetails = target.closest('details')
if (closestDetails) {
const browser = getBrowser()
if (browser.name !== 'Chrome') {
closestDetails.open = true
closestDetails.open = true
// Small delay to ensure the details element is open before scrolling
setTimeout(() => {
target.scrollIntoView({
behavior: 'instant',
behavior: 'smooth',
block: 'start',
})
}
}, 50)
}
}
}
}

// Updates the URL when a dropdown is manually opened/closed
function updateUrlForDropdown(details: HTMLDetailsElement, isOpening: boolean) {
const dropdownId = details.id
if (!dropdownId) return

if (isOpening) {
// Update URL to show the dropdown anchor (like clicking a heading link)
window.history.pushState(null, '', `#${dropdownId}`)
}
// Note: We don't remove the hash when closing, just like headings don't
// This keeps the URL consistent with how headings behave
}

// Initialize the anchor handling functionality
export function initOpenDetailsWithAnchor() {
// Handle initial page load
openDetailsWithAnchor()

// Handle hash changes within the same page (e.g., clicking anchor links)
window.addEventListener('hashchange', openDetailsWithAnchor)

// Handle manual dropdown toggling to update URL
// Use event delegation to catch all toggle events
document.addEventListener(
'toggle',
(event) => {
const target = event.target as HTMLElement

// Check if the target is a details element with dropdown class
if (
target.tagName === 'DETAILS' &&
target.classList.contains('dropdown')
) {
const details = target as HTMLDetailsElement
const isOpening = details.open

// Use setTimeout to ensure the toggle state has been processed
setTimeout(() => {
updateUrlForDropdown(details, isOpening)
}, 0)
}
},
true
) // Use capture phase to ensure we catch the event
}
24 changes: 24 additions & 0 deletions tests/Elastic.Markdown.Tests/Directives/AdmonitionTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -99,3 +99,27 @@ A regular paragraph.
[Fact]
public void SetsDropdownOpen() => Block!.DropdownOpen.Should().BeTrue();
}

public class DropdownWithNameTests(ITestOutputHelper output) : DirectiveTest<AdmonitionBlock>(output,
"""
:::{dropdown} Dropdown with name
:name: test-dropdown
:open:
This is a dropdown with a name
:::
A regular paragraph.
"""
)
{
[Fact]
public void SetsCorrectAdmonitionType() => Block!.Admonition.Should().Be("dropdown");

[Fact]
public void SetsCustomTitle() => Block!.Title.Should().Be("Dropdown with name");

[Fact]
public void SetsDropdownOpen() => Block!.DropdownOpen.Should().BeTrue();

[Fact]
public void SetsCrossReferenceName() => Block!.CrossReferenceName.Should().Be("test-dropdown");
}
Loading