Skip to content

Commit b36c6d8

Browse files
12rambaudrammock
andauthored
fix: resolve path of switcher when using relative (#1344)
* fix: resolve path of switcher when using relative * refactor: silence the console edbug logs * fix: add debug console log for version switcher * fix: found json when the website is using redirection * make version switcher loading work for relative path JSON files * oops forgot to remove console.log cruft * fix: catch the json file even behind a redirect * fix: add the new node to every menu * Apply suggestions from code review * try adding onclick after clone --------- Co-authored-by: Daniel McCloy <[email protected]>
1 parent 347a592 commit b36c6d8

File tree

2 files changed

+86
-61
lines changed

2 files changed

+86
-61
lines changed

src/pydata_sphinx_theme/assets/scripts/pydata-sphinx-theme.js

+83-61
Original file line numberDiff line numberDiff line change
@@ -287,8 +287,8 @@ var setupSearchButtons = () => {
287287
* @param {event} event the event that trigger the check
288288
*/
289289
function checkPageExistsAndRedirect(event) {
290-
const currentFilePath = `${DOCUMENTATION_OPTIONS.pagename}.html`,
291-
tryUrl = event.target.getAttribute("href");
290+
const currentFilePath = `${DOCUMENTATION_OPTIONS.pagename}.html`;
291+
const tryUrl = event.currentTarget.getAttribute("href");
292292
let otherDocsHomepage = tryUrl.replace(currentFilePath, "");
293293

294294
fetch(tryUrl, { method: "HEAD" })
@@ -304,68 +304,90 @@ function checkPageExistsAndRedirect(event) {
304304
return false;
305305
}
306306

307-
// Populate the version switcher from the JSON config file
308-
var themeSwitchBtns = document.querySelectorAll(".version-switcher__button");
309-
if (themeSwitchBtns.length) {
310-
fetch(DOCUMENTATION_OPTIONS.theme_switcher_json_url)
311-
.then((res) => {
312-
return res.json();
313-
})
314-
.then((data) => {
315-
const currentFilePath = `${DOCUMENTATION_OPTIONS.pagename}.html`;
316-
themeSwitchBtns.forEach((btn) => {
317-
// Set empty strings by default so that these attributes exist and can be used in CSS selectors
318-
btn.dataset["activeVersionName"] = "";
319-
btn.dataset["activeVersion"] = "";
307+
/**
308+
* Check if the corresponding url is absolute and make a absolute path from root if necessary
309+
*
310+
* @param {string} url the url to check
311+
*/
312+
async function fetchVersionSwitcherJSON(url) {
313+
// first check if it's a valid URL
314+
try {
315+
var result = new URL(url);
316+
} catch (err) {
317+
// if not, assume relative path and fix accordingly
318+
if (err instanceof TypeError) {
319+
// workaround for redirects like https://pydata-sphinx-theme.readthedocs.io
320+
// fetch() automatically follows redirects so it should work in every builder
321+
// (RDT, GitHub actions, etc)
322+
const origin = await fetch(window.location.origin, {
323+
method: "HEAD",
320324
});
321-
// create links to the corresponding page in the other docs versions
322-
data.forEach((entry) => {
323-
// if no custom name specified (e.g., "latest"), use version string
324-
if (!("name" in entry)) {
325-
entry.name = entry.version;
326-
}
327-
// create the node
328-
const span = document.createElement("span");
329-
span.textContent = `${entry.name}`;
330-
331-
const node = document.createElement("a");
332-
node.setAttribute(
333-
"class",
334-
"list-group-item list-group-item-action py-1"
335-
);
336-
node.setAttribute("href", `${entry.url}${currentFilePath}`);
337-
node.appendChild(span);
338-
339-
// on click, AJAX calls will check if the linked page exists before
340-
// trying to redirect, and if not, will redirect to the homepage
341-
// for that version of the docs.
342-
node.onclick = checkPageExistsAndRedirect;
343-
// Add dataset values for the version and name in case people want
344-
// to apply CSS styling based on this information.
345-
node.dataset["versionName"] = entry.name;
346-
node.dataset["version"] = entry.version;
347-
348-
document.querySelectorAll(".version-switcher__menu").forEach((menu) => {
349-
// There may be multiple version-switcher elements, e.g. one
350-
// in a slide-over panel displayed on smaller screens.
351-
menu.append(node);
352-
});
353-
// replace dropdown button text with the preferred display name of
354-
// this version, rather than using sphinx's {{ version }} variable.
355-
// also highlight the dropdown entry for the currently-viewed
356-
// version's entry
357-
if (
358-
entry.version ==
359-
"DOCUMENTATION_OPTIONS.version_switcher_version_match"
360-
) {
361-
node.classList.add("active");
362-
themeSwitchBtns.forEach((btn) => {
363-
btn.innerText = btn.dataset["activeVersionName"] = entry.name;
364-
btn.dataset["activeVersion"] = entry.version;
365-
});
366-
}
325+
result = new URL(url, origin.url);
326+
} else {
327+
throw err;
328+
}
329+
}
330+
331+
const response = await fetch(result);
332+
const data = await response.json();
333+
return data;
334+
}
335+
336+
// Populate the version switcher from the JSON config file
337+
var versionSwitcherBtns = document.querySelectorAll(
338+
".version-switcher__button"
339+
);
340+
if (versionSwitcherBtns.length) {
341+
const data = await fetchVersionSwitcherJSON(
342+
DOCUMENTATION_OPTIONS.theme_switcher_json_url
343+
);
344+
const currentFilePath = `${DOCUMENTATION_OPTIONS.pagename}.html`;
345+
versionSwitcherBtns.forEach((btn) => {
346+
// Set empty strings by default so that these attributes exist and can be used in CSS selectors
347+
btn.dataset["activeVersionName"] = "";
348+
btn.dataset["activeVersion"] = "";
349+
});
350+
// create links to the corresponding page in the other docs versions
351+
data.forEach((entry) => {
352+
// if no custom name specified (e.g., "latest"), use version string
353+
if (!("name" in entry)) {
354+
entry.name = entry.version;
355+
}
356+
// create the node
357+
const anchor = document.createElement("a");
358+
anchor.setAttribute("class", "list-group-item list-group-item-action py-1");
359+
anchor.setAttribute("href", `${entry.url}${currentFilePath}`);
360+
const span = document.createElement("span");
361+
span.textContent = `${entry.name}`;
362+
anchor.appendChild(span);
363+
// Add dataset values for the version and name in case people want
364+
// to apply CSS styling based on this information.
365+
anchor.dataset["versionName"] = entry.name;
366+
anchor.dataset["version"] = entry.version;
367+
// replace dropdown button text with the preferred display name of
368+
// this version, rather than using sphinx's {{ version }} variable.
369+
// also highlight the dropdown entry for the currently-viewed
370+
// version's entry
371+
if (entry.version == DOCUMENTATION_OPTIONS.version_switcher_version_match) {
372+
anchor.classList.add("active");
373+
versionSwitcherBtns.forEach((btn) => {
374+
btn.innerText = btn.dataset["activeVersionName"] = entry.name;
375+
btn.dataset["activeVersion"] = entry.version;
367376
});
377+
}
378+
// There may be multiple version-switcher elements, e.g. one
379+
// in a slide-over panel displayed on smaller screens.
380+
document.querySelectorAll(".version-switcher__menu").forEach((menu) => {
381+
// we need to clone the node for each menu, but onclick attributes are not
382+
// preserved by `.cloneNode()` so we add onclick here after cloning.
383+
let node = anchor.cloneNode(true);
384+
node.onclick = checkPageExistsAndRedirect;
385+
// on click, AJAX calls will check if the linked page exists before
386+
// trying to redirect, and if not, will redirect to the homepage
387+
// for that version of the docs.
388+
menu.append(node);
368389
});
390+
});
369391
}
370392

371393
/*******************************************************************************

webpack.config.js

+3
Original file line numberDiff line numberDiff line change
@@ -167,4 +167,7 @@ module.exports = {
167167
plugins: [htmlWebpackPlugin, copyPlugin, new MiniCssExtractPlugin({
168168
filename: "styles/[name].css"
169169
})],
170+
experiments: {
171+
topLevelAwait: true,
172+
},
170173
};

0 commit comments

Comments
 (0)