Skip to content

Commit 463e106

Browse files
Flyout and Footer API design document (#8052)
* Flyout and Footer API design document * Update docs/development/design/flyout-redesign.rst Co-authored-by: Manuel Kaufmann <[email protected]> * Update the warning note to be merged --------- Co-authored-by: Manuel Kaufmann <[email protected]>
1 parent aa0ed88 commit 463e106

File tree

3 files changed

+168
-0
lines changed

3 files changed

+168
-0
lines changed
Loading
Loading
Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
===============================
2+
Flyout Redesign Design Document
3+
===============================
4+
5+
.. warning::
6+
7+
This document detailed an initial idea that was not implemented in the end.
8+
Lot of things have changed since this document was written.
9+
A different approach is being implemented as part of the work done on the new addons client at
10+
https://github.com/readthedocs/readthedocs-client
11+
12+
13+
This document describes the design of a new "flyout API"
14+
to replace our existing :ref:`"footer_html" <api/v2:Undocumented resources and endpoints>` endpoint in APIv2.
15+
The Read the Docs theme uses this API to put an updated version selector
16+
with active versions into the lower left menu.
17+
On other themes, this appears as a floating menu in the lower right.
18+
19+
.. contents:: Contents
20+
:local:
21+
:backlinks: none
22+
:depth: 1
23+
24+
25+
Issues with the existing API
26+
------------------------------
27+
28+
.. figure:: ../../_static/images/design-docs/flyout/flyout-expanded.png
29+
:align: right
30+
:figwidth: 300px
31+
:target: ../../_static/images/design-docs/flyout/flyout-expanded.png
32+
33+
Read the Docs with an expanded flyout menu in the lower left corner
34+
35+
* The largest problem with the existing ``/api/v2/footer_html`` endpoint
36+
is that it returns a blob of HTML.
37+
This limits the ability to use this data in ways other than to generate our exact flyout.
38+
For example, Sphinx themes other than our own cannot consume this data easily
39+
and there's no real integration point for MkDocs at all.
40+
* Due to how the URLs in the version selector are generated,
41+
this API endpoint can be fairly expensive in the worst case for projects with many versions.
42+
As it stands now, this API accounts for approximately 15% of the time taken on our webservers
43+
at about 70ms for an average request (P95 ~= 150ms, P99 ~= 235ms).
44+
* The current API is a combination of data about a project that contains information
45+
on the live versions, translations, sub- and super-projects, and links to various other things
46+
like downloads, the repository on the VCS, and builds for the project on RTD itself.
47+
Some of this data never changes (links to the project and builds on RTD itself)
48+
and are effectively static while some of it could change after the documentation is built (translations, active versions).
49+
50+
51+
Overlap with existing API endpoints
52+
-----------------------------------
53+
54+
There is already significant overlap between the APIv2 ``footer_html`` call
55+
with the existing APIv3 for a project (eg. ``https://readthedocs.org/api/v3/projects/docs/``).
56+
The project API already returns much of the data we want,
57+
but some things like other active versions, translations, and downloads would require additional API calls or options.
58+
These options already partially implemented via the `DRF Flex Fields <https://pypi.org/project/drf-flex-fields/>`_ module
59+
(eg. ``https://readthedocs.org/api/v3/projects/docs/?expand=active_versions``).
60+
Currently, there are separate API endpoints for translations and downloads,
61+
but ideally all the data needed to generate a version selector would be available from a single API.
62+
63+
While this is a good approach, it will not be without issues.
64+
It's likely that some database queries and especially URL generation will need to be optimized
65+
for this to not perform worse than the existing footer API.
66+
67+
68+
JavaScript integration
69+
----------------------
70+
71+
Our current footer API is requested from our embedded document JavaScript (``readthedocs-doc-embed.js``)
72+
which is placed on pages during the build process.
73+
Currently, this is a static file that offers no integration points for theme maintainers
74+
or project authors.
75+
There is also some static data about the project injected into the HTML of the generated documentation (``READTHEDOCS_DATA``)
76+
which has some basic data about the project and the specific build being served.
77+
This includes data such as the project and version slugs, the build date, and theme.
78+
79+
One of the goals of this project is to give theme authors and project maintainers integration points
80+
where they can control some aspects of Read the Docs in a repeatable, deterministic way.
81+
In order to do this, we need to have JavaScript constants and functions that these maintainers can set.
82+
This proposal suggests creating a new ``readthedocs`` global variable like so:
83+
84+
.. code-block:: javascript
85+
86+
// This first line helps handle the case where the project/theme's overrides
87+
// are executed before the readthedocs global is defined.
88+
window.readthedocs = window.readthedocs || {};
89+
window.readthedocs.data = JSON.parse(document.getElementById('READTHEDOCS_DATA').innerHTML);
90+
91+
if (!window.readthedocs.integration_version_selector) {
92+
// Set the default flyout menu code
93+
// But only if the user hasn't overridden it already
94+
window.readthedocs.integration_version_selector = function (project) {
95+
// Existing code we use to create the flyout menu
96+
// Currently, this is in `core/static-src/core/js/doc-embed/footer.js:injectFooter`
97+
// The `project` variable is the result of the v3 project API for the current project
98+
};
99+
}
100+
101+
When Read the Docs goes to create the flyout menu,
102+
it will call this new ``readthedocs.integration_version_selector`` method
103+
which will either be our default flyout menu method or the overridden method from the theme or project.
104+
105+
This gives us a baseline of how to add these kinds of integrations.
106+
There are other possible integrations that users may want in the future such as:
107+
108+
* Control how RTD's search overrides Sphinx's built-in search.
109+
This could be used to remove RTD's search overrides entirely
110+
or to just change how they take effect.
111+
* Control how RTD's built-in project analytics sends data.
112+
* There could be an override to how the project's data is retrieved from the API.
113+
This could allow not even getting project/version/translation data at all to save an API call for the project.
114+
115+
116+
Disabling the flyout entirely
117+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
118+
119+
One nice aspect of this integration is that for projects or themes that want to completely disable
120+
the version selector, this could be done by having JS like this:
121+
122+
.. code-block:: javascript
123+
124+
window.readthedocs = window.readthedocs || {};
125+
window.readthedocs.integration_version_selector = function () {};
126+
127+
An alternative would be a way for JS projects to define constants that affects how RTD works.
128+
This could be something like:
129+
130+
.. code-block:: javascript
131+
132+
window.readthedocs = window.readthedocs || {};
133+
window.readthedocs.customizations = {disable_custom_search: true, disable_version_selector: true};
134+
135+
136+
.. figure:: ../../_static/images/design-docs/flyout/flask-versions-mockup.png
137+
:align: right
138+
:figwidth: 300px
139+
:target: ../../_static/images/design-docs/flyout/flask-versions-mockup.png
140+
141+
Flask documentation with a mockup of a custom version selector on the left sidebar
142+
143+
144+
Implementation steps
145+
--------------------
146+
147+
These are the steps that need to be taken to replace our existing footer API v2.
148+
As much as possible, these steps have been setup so they can be done and rolled out independently
149+
so they don't need to be completed all at once.
150+
151+
* Make the changes to APIv3 to allow requesting translations, sub- and super-projects, and downloads.
152+
* Create a feature flag that will make projects use the new APIv3 instead of APIv2.
153+
Set that feature flag on our own projects.
154+
* Modify our embedded document JavaScript to use a new ``readthedocs`` global variable.
155+
If this new feature flag is set, instead of calling the APIv2, the APIv3 will be called
156+
and then ``readthedocs.integration_version_selector`` will be called with the results.
157+
* If all goes well, remove the feature flag and make APIv3 the default and deprecate APIv2.
158+
159+
160+
Future changes not in this rollout
161+
----------------------------------
162+
163+
* Removing the old ``READTHEDOCS_DATA`` variable is not part of this implementation.
164+
This global will continue to be available but removing it will cause some projects to break for sure.
165+
* This proposal doesn't involve creating an integration point to control custom search.
166+
That could happen at a later date.
167+
* This proposal doesn't rework how the version selector looks either on the RTD Sphinx theme
168+
or on other themes by default. Any restyling can be done independently of this.

0 commit comments

Comments
 (0)