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

Use self-contained widgets for UI elements (e.g. the version switcher) #601

Open
choldgraf opened this issue Feb 26, 2022 · 1 comment
Open
Labels
kind: enhancement New feature or request

Comments

@choldgraf
Copy link
Collaborator

choldgraf commented Feb 26, 2022

Context

Currently, we have a number of "widgets" that can be turned on in the navbar of the theme. These are configured with a combination of two things:

  1. Some configuration in html_theme_options. For example, for icon links + a version switcher, as we use in our docs now:
        "icon_links": [
            {
                "name": "PyPI",
                "url": "https://pypi.org/project/pydata-sphinx-theme",
                "icon": "fas fa-box",
            },
            {
                "name": "Pandas",
                "url": "https://pandas.pydata.org",
                "icon": "_static/pandas-square.svg",
                "type": "local",
            },
        ],
        "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/{version}/",
            "version_match": version_match,
        },
  2. Defining an HTML template in some theme location, e.g. for the icon links above:
    html_theme_options = {
          ...
          "navbar_end": ["version-switcher", "navbar-icon-links"],
          ...
    }

Separating out the "configuration" from the "placement" of these components has a couple of downsides.

  1. It means that creating a new kind of UI also means creating a new theme configuration variable in theme.conf, which is already quite long.
  2. It makes it more difficult to re-use the same configuration structure for different parts of the theme (for example, a language switcher is basically the same thing as a version switcher, but with the current setup we'd need to define a new HTML template + configuration variable for it.
  3. Separating the configuration from the placement means keeping track of the same kind of information in two different places, which seems artificial and may lead to confusion.

Since we already ask people to provide a list of templates, we could simplify things by instead letting people provide a list of configurations that would define the elements inserted into that section of the page.

Proposal

Instead of using two configuration variables to configure and place theme UI elements, we instead combine these into a single configuration. For the major UI areas, rather than accepting a list of template names, they would instead accept a list of dictionaries of configuration that corresponds to a type of widget to be inserted.

For example, below would be the same configuration as above, but also shows off how we could define a second dropdown switcher by re-using the same config:

html_theme_options = {
    # Widget configuration for the end of the navbar
    "navbar_end": [
        # First few items are icon links
        {
            "kind": "link",
            "label": "PyPI",
            "url": "https://pypi.org/project/pydata-sphinx-theme",
            "icon": "fas fa-box",
        },
        {
            "kind": "link",
            "label": "Pandas",
            "url": "https://pandas.pydata.org",
            "icon": "_static/pandas-square.svg",
            "type": "local",
        },
        # Now a widget of type "switcher"
        {
            "kind": "switcher",
            "json_url": "https://pydata-sphinx-theme.readthedocs.io/en/latest/_static/switcher.json",
            "version_match": version_match,
        },
        # We could then define a different switcher w/ the same widget type like so:
        {
            "kind": "switcher",
            "json_url": "https://pydata-sphinx-theme.readthedocs.io/en/latest/_static/switcher_language.json",
            "version_match": language_match,
        },
    ]
}

You could also re-use the same widgets in multiple places, with different configurations. For example, if you wanted some icon links in the navbar, but other icon links in the sidebar or footer.

ref: this is inspired by the furo topbar theme, but uses a list of dictionaries so that we could re-use the same widget type multiple times in the same UI area.

How could this be implemented?

One way to implement this would be to define a variable in the HTML context called something like html-widgets. This would be a dictionary of the form <widget-kind> : <jinja macro>. It would work like this:

  • The kind key of our widget configuration would be used to select a widget macro from html-widgets
  • The macro would then accept all other keys for that widget's configuration (other than kind) as key/val pairs
  • The macro would be expected to return a blob of HTML

In our theme templates, we could use this html-widgets dictionary like so:

{% for config in context.get("theme_navbar_end", []) %}
{% set kind = config.pop("kind") %}
{{ context["html-widgets"][kind](**config)
{% endfor %}

With a structure like that, it would also be possible for other packages to piggy-back on the same functionality, by adding their own widget-kind/macro pairs with a Sphinx event, which users could then insert into their theme configuration via the same mechanism described above.

We could define and register these macros like so:

{% macro mywidget(text) %}
{{ text }}
{% endmacro %}
{% context["html-widgets"].append({"mywidget", mywidget}) %}

(I'm not positive this is the right syntax but I think you can do something like this in Jinja.)

Or you could similarly define a function in Python, and add it to the context html-widgets list in the same way. It would just need to output an HTML string.

Longer term

Longer term, I think that it would be best if we tried to standardize these configuration structures across themes, such as Furo and the Book Theme, so that users could port their configurations for common things across themes. This has also already been discussed in the book theme in this issue on pluggable widgets and there's also an implementation to play around with here. I'm opening up the issue in this repository in case we can use this as a chance to prototype some functionality in this theme and see how it feels.

Feedback?

I'd love to hear what people think about the general idea, and about the specific configuration proposed above. Does this seem like it would be useful?

@choldgraf choldgraf added the kind: enhancement New feature or request label Feb 26, 2022
@choldgraf choldgraf changed the title Make our version switcher configuration (and other navbar elements) more self-contained Use self-contained widgets for UI elements (e.g. the version switcher) Feb 26, 2022
@pradyunsg
Copy link
Contributor

pradyunsg commented Feb 27, 2022

@choldgraf pinged me via another channel about this... and this is something I've been thinking about a bunch.

I think what we should do for version switchers is have them be external-to-the-theme, provided to the templates as context variables. The theme can then make its own decisions/choice on how to present the various versions for the user, and provide users with configuration knobs for those things.

The version information can be manually provided by a user through html_context or, more realistically, by a Sphinx extension or RTD's injection logic. That way (a) this would be transferable across themes, (b) would externalise the complexity of "how do I specify multiple versions" away from the theme and into an extension; allowing that to be a replaceable thing and (c) has a clearer separation of concerns.

We effectively have a design for the context variables for this already, in sphinx-multiversion. See also pradyunsg/furo#372 and python/docs-community#4 as well, for context.

x-ref: pradyunsg/sphinx-basic-ng#12

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
kind: enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

2 participants