Skip to content

Commit af02a6a

Browse files
authored
✨ NEW: add version switcher (#436)
Co-authored-by: Daniel McCloy <[email protected]> Co-authored-by: ThuWangzw <[email protected]> Co-authored-by: Xinran Xu <[email protected]> Co-authored-by: MegChai <[email protected]> Co-authored-by: Chris Holdgraf <[email protected]> Co-authored-by: Joris Van den Bossche <[email protected]>
1 parent 102f741 commit af02a6a

File tree

6 files changed

+333
-2
lines changed

6 files changed

+333
-2
lines changed

docs/_static/switcher.json

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
[
2+
{
3+
"name": "v0.7.1 (stable)",
4+
"version": "0.7.1"
5+
},
6+
{
7+
"version": "0.7.0"
8+
},
9+
{
10+
"version": "0.6.3"
11+
},
12+
{
13+
"version": "0.6.2"
14+
},
15+
{
16+
"version": "0.6.1"
17+
},
18+
{
19+
"version": "0.6.0"
20+
},
21+
{
22+
"version": "0.5.2"
23+
},
24+
{
25+
"version": "0.5.1"
26+
},
27+
{
28+
"version": "0.5.0"
29+
},
30+
{
31+
"version": "0.4.3"
32+
},
33+
{
34+
"version": "0.4.2"
35+
},
36+
{
37+
"version": "0.4.1"
38+
},
39+
{
40+
"version": "0.4.0"
41+
},
42+
{
43+
"version": "0.3.2"
44+
},
45+
{
46+
"version": "0.3.1"
47+
},
48+
{
49+
"version": "0.3.0"
50+
}
51+
]

docs/conf.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,8 @@
2424

2525
import pydata_sphinx_theme
2626

27-
version = pydata_sphinx_theme.__version__.replace("dev0", "")
27+
release = pydata_sphinx_theme.__version__
28+
version = release.replace("dev0", "")
2829

2930
# -- General configuration ---------------------------------------------------
3031

@@ -96,8 +97,14 @@
9697
# "navbar_align": "left", # [left, content, right] For testing that the navbar items align properly
9798
# "navbar_start": ["navbar-logo", "navbar-version"],
9899
# "navbar_center": ["navbar-nav", "navbar-version"], # Just for testing
99-
# "navbar_end": ["navbar-icon-links", "navbar-version"] # Just for testing
100+
"navbar_end": ["version-switcher", "navbar-icon-links"],
100101
# "footer_items": ["copyright", "sphinx-version", ""]
102+
"switcher": {
103+
# "json_url": "/_static/switcher.json",
104+
"json_url": "https://pydata-sphinx-theme.readthedocs.io/en/latest/_static/switcher.json",
105+
"url_template": "https://pydata-sphinx-theme.readthedocs.io/en/v{version}/",
106+
"version_match": version,
107+
},
101108
}
102109

103110

docs/user_guide/configuring.rst

Lines changed: 192 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -277,6 +277,198 @@ at the bottom. You can hide these buttons with the following configuration:
277277
}
278278
279279
280+
Add a dropdown to switch between docs versions
281+
==============================================
282+
283+
You can add a button to your site that allows users to
284+
switch between versions of your documentation. The links in the version
285+
switcher will differ depending on which page of the docs is being viewed. For
286+
example, on the page ``https://mysite.org/en/v2.0/changelog.html``, the
287+
switcher links will go to ``changelog.html`` in the other versions of your
288+
docs. When clicked, the switcher will check for the existence of that page, and
289+
if it doesn't exist, redirect to the homepage of that docs version instead.
290+
291+
The switcher requires the following configuration steps:
292+
293+
1. Add a JSON file containing a list of the documentation versions that the
294+
switcher should show on each page.
295+
296+
2. Add a configuration dictionary called ``switcher`` to the
297+
``html_theme_options`` dict in ``conf.py``. ``switcher`` should have 3 keys:
298+
299+
- ``json_url``: the persistent location of the JSON file described above.
300+
- ``url_template``: a template string used to generate the correct URLs for
301+
the different documentation versions.
302+
- ``version_match``: a string stating the version of the documentation that
303+
is currently being browsed.
304+
305+
3. Specify where to place the switcher in your page layout. For example, add
306+
the ``"version-switcher"`` template to one of the layout lists in
307+
``html_theme_options`` (e.g., ``navbar_end``, ``footer_items``, etc).
308+
309+
Below is a more in-depth description of each of these configuration steps.
310+
311+
312+
Add a JSON file to define your switcher's versions
313+
--------------------------------------------------
314+
315+
First, write a JSON file stating which versions of your docs will be listed in
316+
the switcher's dropdown menu. That file should contain a list of entries that
317+
each have one or two fields:
318+
319+
- ``version``: a version string. This will be inserted into
320+
``switcher['url_template']`` to create the links to other docs versions, and
321+
also checked against ``switcher['version_match']`` to provide styling to the
322+
switcher.
323+
- ``name``: an optional name to display in the switcher dropdown instead of the
324+
version string (e.g., "latest", "stable", "dev", etc).
325+
326+
Here is an example JSON file:
327+
328+
.. code:: json
329+
330+
[
331+
{
332+
"name": "v2.1 (stable)",
333+
"version": "2.1"
334+
},
335+
{
336+
"version": "2.0"
337+
},
338+
{
339+
"version": "1.0"
340+
},
341+
]
342+
343+
See the discussion of ``switcher['json_url']`` (below) for options of where to
344+
save the JSON file.
345+
346+
347+
Configure ``switcher['json_url']``
348+
----------------------------------
349+
350+
The JSON file needs to be at a stable, persistent, fully-resolved URL (i.e.,
351+
not specified as a path relative to the sphinx root of the current doc build).
352+
Each version of your documentation should point to the same URL, so that as new
353+
versions are added to the JSON file all the older versions of the docs will
354+
gain switcher dropdown entries linking to the new versions. This could be done
355+
a few different ways:
356+
357+
- The location could be one that is always associated with the most recent
358+
documentation build (i.e., if your docs server aliases "latest" to the most
359+
recent version, it could point to a location in the build tree of version
360+
"latest"). For example:
361+
362+
.. code:: python
363+
364+
html_theme_options = {
365+
...,
366+
"switcher": {
367+
"json_url": "https://mysite.org/en/latest/_static/switcher.json",
368+
}
369+
}
370+
371+
In this case the JSON is versioned alongside the rest of the docs pages but
372+
only the most recent version is ever loaded (even by older versions of the
373+
docs).
374+
375+
- The JSON could be saved in a folder that is listed under your site's
376+
``html_static_path`` configuration. See `the Sphinx static path documentation
377+
<https://www.sphinx-doc.org/en/master/usage/configuration.html#confval-html_static_path>`_
378+
for more information.
379+
380+
- The JSON could be stored outside the doc build trees. This probably means it
381+
would be outside the software repo, and would require you to add new version
382+
entries to the JSON file manually as part of your release process. Example:
383+
384+
.. code:: python
385+
386+
html_theme_options = {
387+
...,
388+
"switcher": {
389+
"json_url": "https://mysite.org/switcher.json",
390+
}
391+
}
392+
393+
394+
Configure ``switcher['url_template']``
395+
--------------------------------------
396+
397+
The switcher's links to other versions of your docs are made by combining the
398+
*version strings* from the JSON file with a *template string* you provide in
399+
``switcher['url_template']``. The template string must contain a placeholder
400+
``{version}`` and otherwise be a fully-resolved URL. For example:
401+
402+
.. code:: python
403+
404+
html_theme_options = {
405+
...,
406+
"switcher": {
407+
"url_template": "https://mysite.org/en/version-{version}/",
408+
}
409+
}
410+
411+
The example above will result in a link to
412+
``https://mysite.org/en/version-1.0/`` for the JSON entry for version
413+
``"1.0"``.
414+
415+
416+
Configure ``switcher['version_match']``
417+
---------------------------------------
418+
419+
This configuration value tells the switcher what docs version is currently
420+
being viewed, and is used to style the switcher (i.e., to highlight the current
421+
docs version in the switcher's dropdown menu, and to change the text displayed
422+
on the switcher button).
423+
424+
Typically you can re-use one of the sphinx variables ``version``
425+
or ``release`` as the value of ``switcher['version_match']``; which one you use
426+
depends on how granular your docs versioning is. See
427+
`the Sphinx "project info" documentation
428+
<https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information>`__
429+
for more information). Example:
430+
431+
.. code:: python
432+
433+
version = my_package_name.__version__.replace("dev0", "") # may differ
434+
html_theme_options = {
435+
...,
436+
"switcher": {
437+
"version_match": version,
438+
}
439+
}
440+
441+
442+
Specify where to display the switcher
443+
-------------------------------------
444+
445+
Finally, tell the theme where on your site's pages you want the switcher to
446+
appear. There are many choices here: you can add ``"version-switcher"`` to one
447+
of the locations in ``html_theme_options`` (e.g., ``navbar_end``,
448+
``footer_items``, etc). For example:
449+
450+
.. code:: python
451+
452+
html_theme_options = {
453+
...,
454+
"navbar_end": ["version-switcher"]
455+
}
456+
457+
458+
Alternatively, you could override one of the other templates to include the
459+
version switcher in a sidebar. For example, you could define
460+
``_templates/sidebar-nav-bs.html`` as:
461+
462+
.. code:: jinja
463+
464+
{%- include 'version-switcher.html' -%}
465+
{{ super() }}
466+
467+
to insert a version switcher at the top of the left sidebar, while still
468+
keeping the default navigation below it. See :doc:`sections` for more
469+
information.
470+
471+
280472
Add an Edit this Page button
281473
============================
282474

docs/user_guide/sections.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,7 @@ will be named accordingly).
118118
- ``sidebar-ethical-ads.html``
119119
- ``sidebar-nav-bs.html``
120120
- ``sphinx-version.html``
121+
- ``version-switcher.html``
121122

122123
Add your own HTML templates to theme sections
123124
=============================================
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
<div class="dropdown" id="version_switcher">
2+
<button type="button" class="btn btn-primary btn-sm navbar-btn dropdown-toggle" id="version_switcher_button" data-toggle="dropdown">
3+
{{ theme_switcher.get('version_match') }} <!-- this text may get changed later by javascript -->
4+
<span class="caret"></span>
5+
</button>
6+
<div id="version_switcher_menu" class="dropdown-menu list-group-flush py-0" aria-labelledby="version_switcher_button">
7+
<!-- dropdown will be populated by javascript on page load -->
8+
</div>
9+
</div>
10+
11+
<!-- NOTE: this JS must live here (not in our global JS file) because it relies
12+
on being processed by Jinja before it is run (specifically for replacing
13+
variables {{ pagename }} and {{ theme_switcher }}.
14+
-->
15+
16+
<script type="text/javascript">
17+
// Construct the target URL from the JSON components
18+
function buildURL(entry) {
19+
var template = "{{ theme_switcher.get('url_template') }}"; // supplied by jinja
20+
template = template.replace("{version}", entry.version);
21+
return template;
22+
}
23+
24+
// Check if corresponding page path exists in other version of docs
25+
// and, if so, go there instead of the homepage of the other docs version
26+
function checkPageExistsAndRedirect(event) {
27+
const currentFilePath = "{{ pagename }}.html",
28+
tryUrl = event.target.getAttribute("href");
29+
let otherDocsHomepage = tryUrl.replace(currentFilePath, "");
30+
$.ajax({
31+
type: 'HEAD',
32+
url: tryUrl,
33+
// if the page exists, go there
34+
success: function() {
35+
location.href = tryUrl;
36+
}
37+
}).fail(function() {
38+
location.href = otherDocsHomepage;
39+
});
40+
// this prevents the browser from following the href of the clicked node
41+
// (which is fine because this function takes care of redirecting)
42+
return false;
43+
}
44+
45+
// Populate the version switcher from the JSON config file
46+
(function () {
47+
$.getJSON("{{ theme_switcher.get('json_url') }}", function(data, textStatus, jqXHR) {
48+
const currentFilePath = "{{ pagename }}.html";
49+
// create links to the corresponding page in the other docs versions
50+
$.each(data, function(index, entry) {
51+
// if no custom name specified (e.g., "latest"), use version string
52+
if (!("name" in entry)) {
53+
entry.name = entry.version;
54+
}
55+
// create the node
56+
const node = document.createElement("a");
57+
node.setAttribute("class", "list-group-item list-group-item-action py-1");
58+
node.textContent = `${entry.name}`;
59+
// get the base URL for that doc version, add the current page's
60+
// path to it, and set as `href`
61+
entry.url = buildURL(entry);
62+
node.setAttribute("href", `${entry.url}${currentFilePath}`);
63+
// on click, AJAX calls will check if the linked page exists before
64+
// trying to redirect, and if not, will redirect to the homepage
65+
// for that version of the docs.
66+
node.onclick = checkPageExistsAndRedirect;
67+
$("#version_switcher_menu").append(node);
68+
// replace dropdown button text with the preferred display name of
69+
// this version, rather than using sphinx's {{ version }} variable.
70+
// also highlight the dropdown entry for the currently-viewed
71+
// version's entry
72+
if (entry.version == "{{ theme_switcher.get('version_match') }}") {
73+
node.classList.add("active");
74+
$("#version_switcher_button").text(entry.name);
75+
}
76+
});
77+
});
78+
})();
79+
</script>

src/pydata_sphinx_theme/theme/pydata_sphinx_theme/theme.conf

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,3 +32,4 @@ navbar_center = navbar-nav.html
3232
navbar_end = navbar-icon-links.html
3333
footer_items = copyright.html, sphinx-version.html
3434
page_sidebar_items = page-toc.html, edit-this-page.html
35+
switcher =

0 commit comments

Comments
 (0)