Skip to content

Commit 7fdc1ea

Browse files
authored
fix(console): scroll docs ToC in JS so <base href> doesn't bounce to home (#1769)
1 parent 6d4cc09 commit 7fdc1ea

1 file changed

Lines changed: 35 additions & 5 deletions

File tree

apps/console/src/pages/DocPage.tsx

Lines changed: 35 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -82,20 +82,49 @@ export default function DocPage() {
8282
};
8383
}, [name, appName, adapter]);
8484

85-
// SPA navigation for rewritten doc-to-doc links: anchors render as
86-
// plain <a href="/docs/...">; intercept same-app clicks so following a
87-
// cross-reference doesn't trigger a full page reload.
85+
// Scroll a heading into view ourselves rather than letting the browser
86+
// follow a bare `#id` href. When the console is served under a sub-path the
87+
// host injects `<base href="…/_console/">` so relative asset URLs resolve; a
88+
// side effect is that fragment-only links resolve against that base instead
89+
// of the current page, so a plain `#id` click navigates to the console root
90+
// ("home") rather than scrolling. Reflect the section in the URL via an
91+
// absolute path that ignores <base>.
92+
const scrollToHeading = useCallback((id: string) => {
93+
document.getElementById(id)?.scrollIntoView({ behavior: 'smooth', block: 'start' });
94+
window.history.replaceState(null, '', `${window.location.pathname}${window.location.search}#${id}`);
95+
}, []);
96+
97+
// SPA navigation for rewritten doc-to-doc links: anchors render as plain
98+
// <a href="/docs/...">; intercept same-app clicks so following a
99+
// cross-reference doesn't trigger a full page reload. In-body fragment links
100+
// (`[x](#section)`) get the same JS-scroll treatment as the ToC so <base>
101+
// doesn't bounce them to home.
88102
const onContentClick = useCallback(
89103
(e: React.MouseEvent<HTMLDivElement>) => {
90104
if (e.defaultPrevented || e.metaKey || e.ctrlKey || e.shiftKey || e.altKey || e.button !== 0) return;
91105
const anchor = (e.target as HTMLElement).closest('a');
92106
const href = anchor?.getAttribute('href');
93-
if (href && href.startsWith('/docs/')) {
107+
if (!href) return;
108+
if (href.startsWith('/docs/')) {
94109
e.preventDefault();
95110
navigate(href);
111+
} else if (href.startsWith('#')) {
112+
e.preventDefault();
113+
scrollToHeading(decodeURIComponent(href.slice(1)));
96114
}
97115
},
98-
[navigate],
116+
[navigate, scrollToHeading],
117+
);
118+
119+
// ToC entries are bare `#id` anchors; scroll in JS for the same <base> reason.
120+
const onTocClick = useCallback(
121+
(e: React.MouseEvent<HTMLAnchorElement>, id: string) => {
122+
// Leave modified / non-primary clicks (new tab, etc.) to the browser.
123+
if (e.defaultPrevented || e.metaKey || e.ctrlKey || e.shiftKey || e.altKey || e.button !== 0) return;
124+
e.preventDefault();
125+
scrollToHeading(id);
126+
},
127+
[scrollToHeading],
99128
);
100129

101130
// Long-doc table of contents (h2–h3). Slugs match rehype-slug so a #id
@@ -161,6 +190,7 @@ export default function DocPage() {
161190
<li key={item.id} style={{ paddingLeft: (item.depth - 2) * 12 }}>
162191
<a
163192
href={`#${item.id}`}
193+
onClick={(e) => onTocClick(e, item.id)}
164194
className="-ml-px block border-l border-transparent py-0.5 pl-3 text-muted-foreground hover:border-primary hover:text-foreground"
165195
>
166196
{item.text}

0 commit comments

Comments
 (0)