Skip to content

fix(chrome): port sidebar / package selector / TOC / preview to plain JS so static deploys work - #26

Merged
Shewart merged 4 commits into
mainfrom
fix/static-chrome-interactivity
Sep 7, 2026
Merged

fix(chrome): port sidebar / package selector / TOC / preview to plain JS so static deploys work#26
Shewart merged 4 commits into
mainfrom
fix/static-chrome-interactivity

Conversation

@Shewart

@Shewart Shewart commented Sep 7, 2026

Copy link
Copy Markdown
Contributor

Summary

Fixes the interactivity gap the 0.1.5-alpha static-prerender pipeline opened up. Four chrome interactions — sidebar section expand/collapse, package selector dropdown, TableOfContents scroll-spy, PreviewFrame source view — were written as ordinary interactive Razor components with @onclick handlers that mutate [Parameter] bool state and re-render via StateHasChanged(). Prerendered HTML captured only the initial state; on a static host with no Blazor runtime, every one of those interactions was dead on the deployed site.

The 0.1.5-alpha CHANGELOG glossed this as "SignalR-backed component state doesn't survive the static build — but a docs site doesn't need it." That was wrong. A docs site's primary navigation surface is the sidebar; if you can't expand a section, you can't reach the pages under it.

This PR ports each interaction to plain JS in shelldocs.js, following the same pattern the four already-working chrome features use (theme toggle, Cmd-K search, code-block copy, CodeGroup tabs). The DOM emits the same initial state Blazor's server-side render produces; delegated document-level click listeners handle the mutations on top. Works identically under a live Blazor runtime (dev mode, Server-hosted deploys) and on plain static hosts (GH Pages, Cloudflare Pages, Netlify).

What's in

Fixed

  • Sidebar section expand/collapse works on static-hosted builds. DocsSidebarNode.razor no longer routes clicks through Blazor's @onclick="Toggle" + _isOpen state. shelldocs.js attaches a delegated click listener on .sidebar-section-toggle and flips [data-open] on the ancestor .sidebar-section and its .sidebar-section-shell child. The initial [data-open] value (from OnParametersSet's active-path check) still comes from server rendering — the ancestor of the current page pre-expands correctly on first paint. CSS unchanged; already selected on [data-open] for both the chevron rotation and the grid-rows animation.

  • Package selector dropdown opens/closes on static-hosted builds. PackageSelector.razor always renders the .pkg-menu now (previously conditional on _open); CSS hides it under display: none unless .pkg[data-open="true"]. Delegated JS handler on .pkg-trigger flips [data-open]; outside-click closes any open menu. Options render as plain <a href={RootUrl}> — native navigation, no Blazor round-trip on selection.

  • TableOfContents scroll-spy attaches on static-hosted builds. Removed the OnAfterRenderAsyncshelldocsToc.attach invocation (which only fires with a live Blazor runtime). The TOC list emits [data-toc-list] + [data-toc-ids="id1,id2,..."]; shelldocs.js scans for these on DOMContentLoaded and after Blazor enhancedload and calls shelldocsToc.attach itself. Anchor click uses native href="#id" navigation.

  • PreviewFrame and ComponentPreview source-view expand/collapse work on static-hosted builds. Removed @onclick="Expand" / Collapse / Show / Hide and the _expanded / _showSource state fields. Buttons carry data-preview-toggle="expand|collapse"; shelldocs.js toggles the same .expanded / .collapsed classes the Blazor state used to toggle. The copy button follows the same pattern ([data-preview-copy]).

How the JS is wired

New shelldocsChrome module in wwwroot/shelldocs.js. Delegated document-level click listeners for the sidebar, package selector, and preview interactions — they survive Blazor's enhanced-nav DOM swap without re-attaching. TOC scroll-spy is the exception: heading IDs change per page, so initToc() re-runs on enhancedload.

Removed (dead code)

  • DocsSidebarNode.Toggle().
  • PackageSelector._open, Toggle(), Choose(), OnBlur().
  • TableOfContents._handle / _sig / Scroll(), IJSObjectReference field, IAsyncDisposable implementation.
  • PreviewFrame._expanded / _copied / _highlighted / _codeEl, Expand() / Collapse() / Copy().
  • ComponentPreview._showSource / _copied / _highlighted / _sourceEl, Show() / Hide() / Copy().

Files

Modified — 9:

  • src/ShellDocs.Components/wwwroot/shelldocs.js — new shelldocsChrome module (~85 lines)
  • src/ShellDocs.Components/Chrome/DocsSidebarNode.razor
  • src/ShellDocs.Components/Chrome/PackageSelector.razor
  • src/ShellDocs.Components/Chrome/PackageSelector.razor.css
  • src/ShellDocs.Components/Chrome/TableOfContents.razor
  • src/ShellDocs.Components/Content/PreviewFrame.razor
  • src/ShellDocs.Components/Content/ComponentPreview.razor
  • CHANGELOG.md — new [0.1.7-alpha] section
  • Directory.Build.props0.1.6-alpha0.1.7-alpha

- Updated ComponentPreview and PreviewFrame components to streamline the expand/collapse functionality and source/code viewing.
- Removed unnecessary state management and JavaScript interop for showing/hiding source and code, replacing it with data attributes for better performance and maintainability.
- Enhanced accessibility by ensuring buttons have appropriate aria-labels and visual feedback for copy actions.
Copilot AI lite review requested due to automatic review settings September 7, 2026 20:51
@Shewart
Shewart merged commit 069b94c into main Sep 7, 2026
2 checks passed

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The preview components now always render both collapsed and expanded UI regions (and both copy/check icons) without corresponding CSS/JS visibility logic, which will produce incorrect UI in practice.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Pull request overview

This PR restores key “chrome” interactivity (sidebar toggles, package dropdown, TOC scroll-spy, and preview source expand/copy) for static-prerendered deployments by moving event handling from Blazor @onclick state to delegated DOM-based JavaScript behavior in shelldocs.js, while updating affected Razor components to emit stable DOM hooks (data-* attributes / CSS classes).

Changes:

  • Added a window.shelldocsChrome JS module with delegated click handlers and TOC auto-init on DOMContentLoaded / enhancedload.
  • Updated Sidebar, PackageSelector, TOC, and Preview components to rely on DOM state (data-open, .collapsed/.expanded, data-preview-*) instead of Blazor runtime state.
  • Bumped version to 0.1.7-alpha and documented the change set in CHANGELOG.md.
File summaries
File Description
src/ShellDocs.Components/wwwroot/shelldocs.js Adds delegated JS handlers for sidebar/package/preview and re-inits TOC on navigation events.
src/ShellDocs.Components/Chrome/DocsSidebarNode.razor Removes Blazor click toggling and relies on JS + data-open for section expand/collapse.
src/ShellDocs.Components/Chrome/PackageSelector.razor Converts dropdown behavior to JS-driven open/close and uses native link navigation for selection.
src/ShellDocs.Components/Chrome/PackageSelector.razor.css Makes menu always renderable and toggles visibility via parent [data-open].
src/ShellDocs.Components/Chrome/TableOfContents.razor Emits data-toc-* for JS to attach scroll-spy without Blazor lifecycle hooks.
src/ShellDocs.Components/Content/PreviewFrame.razor Switches expand/collapse + copy to JS-driven toggles via data-preview-*.
src/ShellDocs.Components/Content/ComponentPreview.razor Switches source expand/collapse + copy to JS-driven toggles via data-preview-*.
CHANGELOG.md Adds a 0.1.7-alpha entry describing the static interactivity fixes and removed code paths.
Directory.Build.props Bumps package version from 0.1.6-alpha to 0.1.7-alpha.
Review details
  • Files reviewed: 9/9 changed files
  • Comments generated: 5
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +10 to 14
<button type="button" class="pkg-trigger" aria-haspopup="listbox" aria-expanded="false">
<span class="pkg-mark">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round">
<path d="@(Selected.IconPath ?? DefaultIcon)"/>
</svg>
Comment on lines +23 to +33
<pre class="component-preview-source language-razor"><code class="language-razor">@_source</code></pre>
<div class="component-preview-fade">
<button type="button" class="component-preview-expand" data-preview-toggle="expand">View source</button>
</div>
<div class="component-preview-actions">
<button type="button" class="component-preview-copy" data-preview-copy aria-label="Copy source">
<svg class="icon-copy" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="12" height="12" rx="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg>
<svg class="icon-check" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg>
</button>
<button type="button" class="component-preview-hide" data-preview-toggle="collapse">Hide</button>
</div>
Comment on lines +27 to +37
<pre class="preview-code language-razor"><code class="language-razor">@Preview!.Code</code></pre>
<div class="preview-fade">
<button type="button" class="preview-expand" data-preview-toggle="expand">View Code</button>
</div>
<div class="preview-code-actions">
<button type="button" class="preview-copy" data-preview-copy aria-label="Copy code">
<svg class="icon-copy" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="12" height="12" rx="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg>
<svg class="icon-check" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg>
</button>
<button type="button" class="preview-collapse" data-preview-toggle="collapse">Collapse</button>
</div>
Comment on lines +295 to +305
var code = frame.querySelector('pre code');
if (!code) return;
var text = code.innerText;
var writeText = navigator.clipboard && navigator.clipboard.writeText
? navigator.clipboard.writeText(text)
: Promise.reject(new Error('clipboard unavailable'));
writeText.then(function () {
copy.classList.add('copied');
setTimeout(function () { copy.classList.remove('copied'); }, 1400);
}).catch(function () { /* silent */ });
}
Comment on lines +98 to 100
// Toggle handled by shelldocs.js — kept as a no-op just in case the
// Blazor lifecycle re-renders and needs the initial state stable.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants