Skip to content

Commit 1f927f1

Browse files
Add region selector for multi-region documentation URLs (#2086)
* Add region selector toggle to docs Adds a "Select your region" toggle (US/EU/AP) at the top of any documentation page that references platform.robusta.dev or api.robusta.dev. Selecting EU or AP rewrites those hosts in-place to platform.<region>.robusta.dev and api.<region>.robusta.dev (covers prose, code blocks, and anchor hrefs). Choice is persisted in localStorage so it carries across pages. The toggle is injected by region-selector.js and only appears on pages that actually contain matching URLs. * Fix region selector button styles under Material theme The Material theme's .md-typeset button reset was more specific than the class-only selectors, so the buttons rendered as bare text. Scope the rules under .md-typeset and pin the visual properties with !important so the toggle renders as a proper segmented control in both light and dark modes. Persistence across pages was already handled via localStorage; no JS changes needed. * Make region selector idempotent and instant-nav aware - init() now clears the targets array and removes any existing toggle before re-injecting, so it can be called repeatedly without duplicating UI or leaking stale node references. - Subscribe to the theme's document$ observable so the toggle is re-injected after Material/Sphinx-Immaterial instant navigations, which swap the content area without firing DOMContentLoaded. - Always call applyRegion on init (including "us") so any stored selection is consistently reapplied after navigation. * Replace page-header region toggle with per-component selectors Introduces two RST directives for embedding region-aware Robusta URLs and code blocks inline in the docs: .. robusta-url:: https://api.robusta.dev/api/alerts .. robusta-code:: bash curl https://api.robusta.dev/api/alerts -H 'Authorization: ...' Each rendered component (".robusta-region-box") now carries its own US / EU / AP selector at the top of its frame. Clicking any selector on a page syncs every other component on the same page in lock-step and persists the choice to localStorage so it sticks across navigation. Implementation: - New Sphinx extension docs/_ext/region_box.py defines RobustaUrlDirective and RobustaCodeDirective and is registered in conf.py. - region-selector.js no longer injects a page-header toggle. It scans for .robusta-region-box elements, injects an inline selector bar into each, collects URL targets from the box body, and exposes a single syncAll() that re-applies the chosen region across every box. - custom.css restyles the toggle as a tight segmented control sitting flush atop the URL / code frame. - send-alerts-api.rst is migrated to use both directives as a worked example. Remaining docs continue to render their original URLs as-is until they are migrated to the new directives. * Rename selector label to 'Select Region' and migrate all docs Two changes: 1. Rename the bar label from "Region" to "Select Region" in the selector UI. 2. Migrate every documentation page that mentions platform.robusta.dev or api.robusta.dev so the region selector is visible wherever the user encounters a Robusta endpoint. Concretely, replace .. code-block:: <lang> with .. robusta-code:: <lang> for any code block whose body contains a Robusta URL — 65 blocks across 36 files. Pages whose only mention is an inline external link (signup CTAs, prose references) are intentionally left as-is; the page-wide JS still rewrites those URLs to the selected region using the value persisted in localStorage from any page that does carry a selector. The JS also now collects URL targets from the whole content area rather than scoping them to each box, so a selector click on any component on the page rewrites every Robusta URL it can reach (prose, tables, code, anchor hrefs). * Stop uppercasing the 'Select Region' label * Add :robusta-url: inline role and migrate remaining inline-only docs Introduces an inline counterpart to the .. robusta-url:: / .. robusta-code:: directives, so prose mentions of Robusta URLs get their own region picker without needing a block-level component: Sign up at :robusta-url:`https://platform.robusta.dev/signup` today. Or with custom link text: Visit :robusta-url:`our platform <https://platform.robusta.dev/signup>`. The role emits a span.robusta-region-inline wrapping the link; the client-side script appends a compact US/EU/AP picker that shares state with every other region selector on the page (boxes and inline alike). Migrated 15 docs whose only Robusta URL mention was an inline external link or inline literal — signup CTAs in install / architecture / playbook / pro-features / metric-providers / RobustaUI / oss-vs-saas / help / routing-with-scopes / _see_robusta_in_action-2 and the integration endpoint references in alertmanager-integration/{dynatrace, gcp-monitoring, launchdarkly, newrelic}.rst — all now use the role and display the region picker beside the link. The sphinx_design .. button-link:: on the docs index page is left as a plain URL; the page-wide JS still rewrites its href silently so the CTA points at the user's chosen region. * Add region picker above the index page Get Started button * Turn the inline region selector into a dropdown menu Replaces the segmented US|EU|AP buttons on each .robusta-region-inline with a single trigger pill (showing the current region + caret) that opens a small dropdown listbox on click. State stays in sync with every other region selector on the page via the existing syncAll() plumbing. The menu closes on outside-click, on Escape, and when another inline picker is opened. Also adds a .. robusta-region-picker:: directive that emits a "Select Region" label plus a standalone dropdown, no URL display. The docs index page now uses this directive above the Get Started button instead of the previous :robusta-url: line, since the button's href is already region-aware and showing the URL again was redundant. * Fix inline dropdown not closing and width misalignment Material's .md-typeset ul styles overrode the menu's display: none, leaving the dropdown permanently visible. Pin display with !important in both the closed and open states. Also tighten the menu to match its trigger: min-width: 100% (of the picker), narrower item padding, and centered labels so the menu sits flush under the AP/EU/US pill instead of mushrooming out to a fixed 4rem width. --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent 13c5873 commit 1f927f1

56 files changed

Lines changed: 837 additions & 84 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

docs/_ext/region_box.py

Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
"""
2+
Directives and role for Robusta platform URL / code components with an
3+
embedded region selector.
4+
5+
Usage in .rst::
6+
7+
.. robusta-url:: https://platform.robusta.dev/account
8+
9+
.. robusta-code:: bash
10+
11+
curl --location --request POST 'https://api.robusta.dev/api/alerts' \
12+
--header 'Authorization: Bearer API-KEY'
13+
14+
Sign up at :robusta-url:`https://platform.robusta.dev/signup` to get started.
15+
16+
Or with custom link text:
17+
18+
Visit :robusta-url:`our platform <https://platform.robusta.dev/signup>` today.
19+
20+
The rendered HTML carries ``robusta-region-box`` (block) or
21+
``robusta-region-inline`` (inline) classes; the client-side script in
22+
``_static/region-selector.js`` injects the US/EU/AP selector and keeps
23+
every region-aware component on the page in sync.
24+
"""
25+
26+
import re
27+
from html import escape
28+
29+
from docutils import nodes
30+
from sphinx.util.docutils import SphinxDirective
31+
32+
33+
_LABELLED_URL_RE = re.compile(r"^\s*(.+?)\s*<\s*([^<>\s]+)\s*>\s*$", re.DOTALL)
34+
35+
36+
class RobustaUrlDirective(SphinxDirective):
37+
has_content = True
38+
required_arguments = 0
39+
optional_arguments = 1
40+
final_argument_whitespace = True
41+
42+
def run(self):
43+
if self.arguments:
44+
url = self.arguments[0].strip()
45+
else:
46+
url = "\n".join(self.content).strip()
47+
if not url:
48+
return [
49+
self.state.document.reporter.warning(
50+
"robusta-url requires a URL argument or content",
51+
line=self.lineno,
52+
)
53+
]
54+
55+
url_attr = escape(url, quote=True)
56+
url_text = escape(url)
57+
html = (
58+
f'<div class="robusta-region-box robusta-region-box--url">'
59+
f'<div class="robusta-region-box__body">'
60+
f'<a class="robusta-region-box__url" href="{url_attr}">{url_text}</a>'
61+
f'</div></div>'
62+
)
63+
return [nodes.raw("", html, format="html")]
64+
65+
66+
class RobustaCodeDirective(SphinxDirective):
67+
has_content = True
68+
required_arguments = 0
69+
optional_arguments = 1
70+
final_argument_whitespace = False
71+
72+
def run(self):
73+
language = self.arguments[0] if self.arguments else "text"
74+
code = "\n".join(self.content)
75+
if not code.strip():
76+
return [
77+
self.state.document.reporter.warning(
78+
"robusta-code requires a code block as content",
79+
line=self.lineno,
80+
)
81+
]
82+
83+
container = nodes.container(classes=["robusta-region-box", "robusta-region-box--code"])
84+
literal = nodes.literal_block(code, code)
85+
literal["language"] = language
86+
container += literal
87+
return [container]
88+
89+
90+
class RobustaRegionPickerDirective(SphinxDirective):
91+
"""Standalone region picker: a 'Select Region' label + dropdown, no URL."""
92+
93+
has_content = False
94+
required_arguments = 0
95+
optional_arguments = 1
96+
final_argument_whitespace = True
97+
98+
def run(self):
99+
label = (self.arguments[0].strip() if self.arguments else "Select Region")
100+
html = (
101+
f'<div class="robusta-region-picker">'
102+
f'<span class="robusta-region-picker__label">{escape(label)}</span>'
103+
f'<span class="robusta-region-inline robusta-region-picker__host"></span>'
104+
f"</div>"
105+
)
106+
return [nodes.raw("", html, format="html")]
107+
108+
109+
def robusta_url_role(name, rawtext, text, lineno, inliner, options=None, content=None):
110+
"""Inline ``:robusta-url:`URL``` or ``:robusta-url:`label <URL>```."""
111+
raw_text = nodes.unescape(text)
112+
match = _LABELLED_URL_RE.match(raw_text)
113+
if match:
114+
label = match.group(1).strip()
115+
url = match.group(2).strip()
116+
else:
117+
url = raw_text.strip()
118+
label = url
119+
if not label:
120+
label = url
121+
if not url:
122+
msg = inliner.reporter.error(
123+
"robusta-url role requires a URL", line=lineno
124+
)
125+
return [inliner.problematic(rawtext, rawtext, msg)], [msg]
126+
127+
html = (
128+
f'<span class="robusta-region-inline">'
129+
f'<a class="robusta-region-inline__url" href="{escape(url, quote=True)}">'
130+
f"{escape(label)}</a>"
131+
f"</span>"
132+
)
133+
return [nodes.raw("", html, format="html")], []
134+
135+
136+
def setup(app):
137+
app.add_directive("robusta-url", RobustaUrlDirective)
138+
app.add_directive("robusta-code", RobustaCodeDirective)
139+
app.add_directive("robusta-region-picker", RobustaRegionPickerDirective)
140+
app.add_role("robusta-url", robusta_url_role)
141+
return {
142+
"version": "0.3",
143+
"parallel_read_safe": True,
144+
"parallel_write_safe": True,
145+
}

0 commit comments

Comments
 (0)