Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

✨ NEW: add version switcher #436

Merged
merged 28 commits into from Dec 3, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
84d0562
add version switcher (cleaner diff this time)
drammock Jul 28, 2021
5f4a023
remove unreachable ancient versions
drammock Aug 10, 2021
93b0aa0
fallback on version if name missing
drammock Aug 10, 2021
e913a48
simplify redundancies in switcher.json
drammock Aug 10, 2021
cefafeb
separate out version switcher
drammock Sep 3, 2021
0f12757
remove language-related stuff
drammock Oct 25, 2021
0ff914e
add recent versions to JSON
drammock Oct 25, 2021
852cc4e
do AJAX onclick, not in advance
drammock Oct 25, 2021
ac2a57b
use href instead of data-href
drammock Oct 26, 2021
26e5988
better comments
drammock Oct 27, 2021
0328868
update the dropdown button text
drammock Oct 27, 2021
2a38901
fix url shown on hover
drammock Oct 27, 2021
768a76e
fix template URL
drammock Oct 27, 2021
5318996
change display name "latest" → "stable"
drammock Oct 27, 2021
ab261a3
add ID to div
drammock Oct 28, 2021
def2d67
document the switcher
drammock Oct 28, 2021
d968eef
change default button text to be sphinx "version" variable
drammock Oct 28, 2021
fec276c
add comment about JS needing jinja
drammock Oct 30, 2021
24da28e
doc rewrite
drammock Nov 1, 2021
407159d
better variable name, remove vars from theme.conf, style current vers…
drammock Nov 1, 2021
f009b9d
minor cleanups
drammock Nov 1, 2021
87c0dc9
use html_theme_options instead of html_context
drammock Nov 1, 2021
191a847
fix copy-paste error
drammock Nov 1, 2021
e683a50
better code comments
drammock Nov 1, 2021
3258028
make switcher config a single dict
drammock Nov 2, 2021
9ad61e2
Merge remote-tracking branch 'upstream/master' into redo-switcher
jorisvandenbossche Dec 3, 2021
a010d24
switch url
jorisvandenbossche Dec 3, 2021
865ed64
prettier json
jorisvandenbossche Dec 3, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
51 changes: 51 additions & 0 deletions docs/_static/switcher.json
@@ -0,0 +1,51 @@
[
drammock marked this conversation as resolved.
Show resolved Hide resolved
{
"name": "v0.7.1 (stable)",
"version": "0.7.1"
},
{
"version": "0.7.0"
},
{
"version": "0.6.3"
},
{
"version": "0.6.2"
},
{
"version": "0.6.1"
},
{
"version": "0.6.0"
},
{
"version": "0.5.2"
},
{
"version": "0.5.1"
},
{
"version": "0.5.0"
},
{
"version": "0.4.3"
},
{
"version": "0.4.2"
},
{
"version": "0.4.1"
},
{
"version": "0.4.0"
},
{
"version": "0.3.2"
},
{
"version": "0.3.1"
},
{
"version": "0.3.0"
}
]
11 changes: 9 additions & 2 deletions docs/conf.py
Expand Up @@ -24,7 +24,8 @@

import pydata_sphinx_theme

version = pydata_sphinx_theme.__version__.replace("dev0", "")
release = pydata_sphinx_theme.__version__
version = release.replace("dev0", "")

# -- General configuration ---------------------------------------------------

Expand Down Expand Up @@ -96,8 +97,14 @@
# "navbar_align": "left", # [left, content, right] For testing that the navbar items align properly
# "navbar_start": ["navbar-logo", "navbar-version"],
# "navbar_center": ["navbar-nav", "navbar-version"], # Just for testing
# "navbar_end": ["navbar-icon-links", "navbar-version"] # Just for testing
"navbar_end": ["version-switcher", "navbar-icon-links"],
# "footer_items": ["copyright", "sphinx-version", ""]
"switcher": {
# "json_url": "/_static/switcher.json",
"json_url": "https://pydata-sphinx-theme.readthedocs.io/en/latest/_static/switcher.json",
"url_template": "https://pydata-sphinx-theme.readthedocs.io/en/v{version}/",
"version_match": version,
},
}


Expand Down
192 changes: 192 additions & 0 deletions docs/user_guide/configuring.rst
Expand Up @@ -277,6 +277,198 @@ at the bottom. You can hide these buttons with the following configuration:
}


Add a dropdown to switch between docs versions
==============================================

You can add a button to your site that allows users to
switch between versions of your documentation. The links in the version
switcher will differ depending on which page of the docs is being viewed. For
example, on the page ``https://mysite.org/en/v2.0/changelog.html``, the
switcher links will go to ``changelog.html`` in the other versions of your
docs. When clicked, the switcher will check for the existence of that page, and
if it doesn't exist, redirect to the homepage of that docs version instead.

The switcher requires the following configuration steps:

1. Add a JSON file containing a list of the documentation versions that the
switcher should show on each page.

2. Add a configuration dictionary called ``switcher`` to the
``html_theme_options`` dict in ``conf.py``. ``switcher`` should have 3 keys:

- ``json_url``: the persistent location of the JSON file described above.
- ``url_template``: a template string used to generate the correct URLs for
the different documentation versions.
- ``version_match``: a string stating the version of the documentation that
is currently being browsed.

3. Specify where to place the switcher in your page layout. For example, add
the ``"version-switcher"`` template to one of the layout lists in
``html_theme_options`` (e.g., ``navbar_end``, ``footer_items``, etc).

Below is a more in-depth description of each of these configuration steps.


Add a JSON file to define your switcher's versions
--------------------------------------------------

First, write a JSON file stating which versions of your docs will be listed in
the switcher's dropdown menu. That file should contain a list of entries that
each have one or two fields:

- ``version``: a version string. This will be inserted into
``switcher['url_template']`` to create the links to other docs versions, and
also checked against ``switcher['version_match']`` to provide styling to the
switcher.
- ``name``: an optional name to display in the switcher dropdown instead of the
version string (e.g., "latest", "stable", "dev", etc).

Here is an example JSON file:

.. code:: json

[
{
"name": "v2.1 (stable)",
"version": "2.1"
},
{
"version": "2.0"
},
{
"version": "1.0"
},
]

See the discussion of ``switcher['json_url']`` (below) for options of where to
save the JSON file.


Configure ``switcher['json_url']``
----------------------------------

The JSON file needs to be at a stable, persistent, fully-resolved URL (i.e.,
not specified as a path relative to the sphinx root of the current doc build).
Each version of your documentation should point to the same URL, so that as new
versions are added to the JSON file all the older versions of the docs will
gain switcher dropdown entries linking to the new versions. This could be done
a few different ways:

- The location could be one that is always associated with the most recent
documentation build (i.e., if your docs server aliases "latest" to the most
recent version, it could point to a location in the build tree of version
"latest"). For example:

.. code:: python

html_theme_options = {
...,
"switcher": {
"json_url": "https://mysite.org/en/latest/_static/switcher.json",
}
}

In this case the JSON is versioned alongside the rest of the docs pages but
only the most recent version is ever loaded (even by older versions of the
docs).

- The JSON could be saved in a folder that is listed under your site's
``html_static_path`` configuration. See `the Sphinx static path documentation
<https://www.sphinx-doc.org/en/master/usage/configuration.html#confval-html_static_path>`_
for more information.

- The JSON could be stored outside the doc build trees. This probably means it
would be outside the software repo, and would require you to add new version
entries to the JSON file manually as part of your release process. Example:

.. code:: python

html_theme_options = {
...,
"switcher": {
"json_url": "https://mysite.org/switcher.json",
}
}


Configure ``switcher['url_template']``
--------------------------------------

The switcher's links to other versions of your docs are made by combining the
*version strings* from the JSON file with a *template string* you provide in
``switcher['url_template']``. The template string must contain a placeholder
``{version}`` and otherwise be a fully-resolved URL. For example:

.. code:: python

html_theme_options = {
...,
"switcher": {
"url_template": "https://mysite.org/en/version-{version}/",
}
}

The example above will result in a link to
``https://mysite.org/en/version-1.0/`` for the JSON entry for version
``"1.0"``.


Configure ``switcher['version_match']``
---------------------------------------

This configuration value tells the switcher what docs version is currently
being viewed, and is used to style the switcher (i.e., to highlight the current
docs version in the switcher's dropdown menu, and to change the text displayed
on the switcher button).

Typically you can re-use one of the sphinx variables ``version``
or ``release`` as the value of ``switcher['version_match']``; which one you use
depends on how granular your docs versioning is. See
`the Sphinx "project info" documentation
<https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information>`__
for more information). Example:

.. code:: python

version = my_package_name.__version__.replace("dev0", "") # may differ
html_theme_options = {
...,
"switcher": {
"version_match": version,
}
}


Specify where to display the switcher
-------------------------------------

Finally, tell the theme where on your site's pages you want the switcher to
appear. There are many choices here: you can add ``"version-switcher"`` to one
of the locations in ``html_theme_options`` (e.g., ``navbar_end``,
``footer_items``, etc). For example:

.. code:: python

html_theme_options = {
...,
"navbar_end": ["version-switcher"]
}


Alternatively, you could override one of the other templates to include the
version switcher in a sidebar. For example, you could define
``_templates/sidebar-nav-bs.html`` as:

.. code:: jinja

{%- include 'version-switcher.html' -%}
{{ super() }}

to insert a version switcher at the top of the left sidebar, while still
keeping the default navigation below it. See :doc:`sections` for more
information.


Add an Edit this Page button
============================

Expand Down
1 change: 1 addition & 0 deletions docs/user_guide/sections.rst
Expand Up @@ -118,6 +118,7 @@ will be named accordingly).
- ``sidebar-ethical-ads.html``
- ``sidebar-nav-bs.html``
- ``sphinx-version.html``
- ``version-switcher.html``

Add your own HTML templates to theme sections
=============================================
Expand Down
@@ -0,0 +1,79 @@
<div class="dropdown" id="version_switcher">
<button type="button" class="btn btn-primary btn-sm navbar-btn dropdown-toggle" id="version_switcher_button" data-toggle="dropdown">
{{ theme_switcher.get('version_match') }} <!-- this text may get changed later by javascript -->
<span class="caret"></span>
</button>
<div id="version_switcher_menu" class="dropdown-menu list-group-flush py-0" aria-labelledby="version_switcher_button">
<!-- dropdown will be populated by javascript on page load -->
</div>
</div>

<!-- NOTE: this JS must live here (not in our global JS file) because it relies
on being processed by Jinja before it is run (specifically for replacing
variables {{ pagename }} and {{ theme_switcher }}.
-->

<script type="text/javascript">
// Construct the target URL from the JSON components
function buildURL(entry) {
var template = "{{ theme_switcher.get('url_template') }}"; // supplied by jinja
template = template.replace("{version}", entry.version);
return template;
}

// Check if corresponding page path exists in other version of docs
// and, if so, go there instead of the homepage of the other docs version
function checkPageExistsAndRedirect(event) {
const currentFilePath = "{{ pagename }}.html",
tryUrl = event.target.getAttribute("href");
let otherDocsHomepage = tryUrl.replace(currentFilePath, "");
$.ajax({
type: 'HEAD',
url: tryUrl,
// if the page exists, go there
success: function() {
location.href = tryUrl;
}
}).fail(function() {
location.href = otherDocsHomepage;
});
// this prevents the browser from following the href of the clicked node
// (which is fine because this function takes care of redirecting)
return false;
}

// Populate the version switcher from the JSON config file
(function () {
$.getJSON("{{ theme_switcher.get('json_url') }}", function(data, textStatus, jqXHR) {
const currentFilePath = "{{ pagename }}.html";
// create links to the corresponding page in the other docs versions
$.each(data, function(index, entry) {
// if no custom name specified (e.g., "latest"), use version string
if (!("name" in entry)) {
entry.name = entry.version;
}
// create the node
const node = document.createElement("a");
node.setAttribute("class", "list-group-item list-group-item-action py-1");
node.textContent = `${entry.name}`;
// get the base URL for that doc version, add the current page's
// path to it, and set as `href`
entry.url = buildURL(entry);
node.setAttribute("href", `${entry.url}${currentFilePath}`);
// on click, AJAX calls will check if the linked page exists before
// trying to redirect, and if not, will redirect to the homepage
// for that version of the docs.
node.onclick = checkPageExistsAndRedirect;
$("#version_switcher_menu").append(node);
// replace dropdown button text with the preferred display name of
// this version, rather than using sphinx's {{ version }} variable.
// also highlight the dropdown entry for the currently-viewed
// version's entry
if (entry.version == "{{ theme_switcher.get('version_match') }}") {
node.classList.add("active");
$("#version_switcher_button").text(entry.name);
}
});
});
})();
</script>
Expand Up @@ -32,3 +32,4 @@ navbar_center = navbar-nav.html
navbar_end = navbar-icon-links.html
footer_items = copyright.html, sphinx-version.html
page_sidebar_items = page-toc.html, edit-this-page.html
switcher =