Add region selector for multi-region documentation URLs#2086
Conversation
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.
|
✅ Docker image ready for
Use this tag to pull the image for testing. 📋 Copy commandsgcloud auth configure-docker us-central1-docker.pkg.dev
docker pull us-central1-docker.pkg.dev/robusta-development/temporary-builds/robusta-runner:3ffb2da
docker tag us-central1-docker.pkg.dev/robusta-development/temporary-builds/robusta-runner:3ffb2da me-west1-docker.pkg.dev/robusta-development/development/robusta-runner-dev:3ffb2da
docker push me-west1-docker.pkg.dev/robusta-development/development/robusta-runner-dev:3ffb2daPatch Helm values in one line: helm upgrade --install robusta robusta/robusta \
--reuse-values \
--set runner.image=me-west1-docker.pkg.dev/robusta-development/development/robusta-runner-dev:3ffb2da |
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (2)
WalkthroughAdds Sphinx directives and client-side assets for a persisted region selector that injects UI into documentation, rewrites Robusta platform/api URLs per selected region, includes CSS styling, and registers the script in Sphinx output. ChangesRegion Selector Feature
Sequence Diagram(s)sequenceDiagram
participant Init as Initialization
participant Storage as localStorage
participant Document as Document (text nodes & attributes)
participant UI as Region Selector UI
participant User as User
Init->>Storage: read stored region (default "us")
Init->>Document: collect matching text nodes and attribute targets
Init->>Document: apply stored region rewrites
Init->>UI: inject Region Selector UI into `.robusta-region-box` / `.robusta-region-inline`
User->>UI: click region button / select option
UI->>Storage: save selected region
UI->>Document: reapply URL rewrite for new region
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Suggested reviewers
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@docs/_static/region-selector.js`:
- Line 151: The initial-load branch currently skips calling applyRegion when
region === "us", leaving authored hostnames like
platform.eu.robusta.dev/api.ap.robusta.dev unnormalized; remove the conditional
and invoke applyRegion on initial load for all regions (i.e., always call
applyRegion(region)) or modify applyRegion to be explicitly invoked for the "us"
case so that region and applyRegion are used unconditionally during
initialization.
- Around line 154-158: The current init() is only run on initial load and must
be re-run for Material/Sphinx-Immaterial instant navigations; update the module
so init() is idempotent (clear the targets array and, before inserting the
toggle, detect and remove/replace any existing element with class
.robusta-region-selector) and add a listener into the Material instant-nav
lifecycle in addition to the DOMContentLoaded gate so init() is invoked on each
client-side instant navigation (hook into the theme's instant navigation event
and call init() there).
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 7f45e480-c3c3-4cb2-9e61-312c49629ac5
📒 Files selected for processing (3)
docs/_static/custom.cssdocs/_static/region-selector.jsdocs/conf.py
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.
- 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.
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
docs/_static/region-selector.js (1)
100-121:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winAdd WAI-ARIA keyboard navigation to the region selector radiogroup
docs/_static/region-selector.jssets uprole="radiogroup"/role="radio"and updatesaria-checked, but only supports mouse viaclick(nokeydownfor Arrow keys, and notabindex/focus management for moving between options). Implement the APG radio pattern so ArrowLeft/Right (and ArrowUp/Down) move focus (wrapping) and update the checked state (and.is-active), reusing the existing selection logic for both click and key interaction.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@docs/_static/region-selector.js` around lines 100 - 121, The region selector only handles mouse clicks; add keyboard support by setting focus management and key handlers on each button created in the loop (use the existing variables group, btn, REGIONS, current). Ensure one radio has tabindex="0" (the active one) and others tabindex="-1", update tabindexes when selection changes, and add a keydown listener on btn to handle ArrowLeft/ArrowRight and ArrowUp/ArrowDown (wrap around indices obtained from group.querySelectorAll("button[data-region]")), moving focus to the next/previous button and invoking the same selection logic (call saveRegion/applyRegion and toggle .is-active and aria-checked) so keyboard navigation reuses the existing click-selection behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Outside diff comments:
In `@docs/_static/region-selector.js`:
- Around line 100-121: The region selector only handles mouse clicks; add
keyboard support by setting focus management and key handlers on each button
created in the loop (use the existing variables group, btn, REGIONS, current).
Ensure one radio has tabindex="0" (the active one) and others tabindex="-1",
update tabindexes when selection changes, and add a keydown listener on btn to
handle ArrowLeft/ArrowRight and ArrowUp/ArrowDown (wrap around indices obtained
from group.querySelectorAll("button[data-region]")), moving focus to the
next/previous button and invoking the same selection logic (call
saveRegion/applyRegion and toggle .is-active and aria-checked) so keyboard
navigation reuses the existing click-selection behavior.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: bbe39361-f5e9-4e02-8c56-3f9fa15a942f
📒 Files selected for processing (1)
docs/_static/region-selector.js
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.
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).
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.
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.
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.
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@docs/_ext/region_box.py`:
- Around line 114-119: The parsing path that sets label = match.group(1).strip()
can yield an empty string and produce an empty anchor; after extracting label
and url (from match.group(...) or raw_text), check if label is falsy (empty
after strip) and fallback to using the url (or url.strip()) as the label; update
the code paths that set label/url (the match.group(...) branch and the raw_text
branch) so both perform this fallback to avoid rendering empty inline link text.
In `@docs/_static/region-selector.js`:
- Around line 147-166: The inline picker options are not keyboard-operable
because tabIndex is set to -1 and only a click handler is attached; update the
option creation in the REGIONS iteration to make items focusable and navigable
by keyboard (reference: variables/functions REGIONS, currentRegion, items, menu,
picker, trigger, syncAll). Set tabIndex so options can receive focus (e.g., 0
when picker is open, or manage focus programmatically), add a keydown handler on
each item to handle Enter/Space to invoke syncAll(key) and close the picker
(mirroring the click handler), and handle ArrowUp/ArrowDown/Home/End to move
focus between items and update aria-selected/class (is-active) accordingly so
keyboard users can navigate and select options the same way mouse users do.
Ensure after selection you remove "is-open" from picker, set trigger
aria-expanded="false", and focus the trigger.
In `@docs/configuration/alertmanager-integration/launchdarkly.rst`:
- Line 46: Update the LaunchDarkly webhook docs in
docs/configuration/alertmanager-integration/launchdarkly.rst to remove the
misleading "Headers: Authorization: Bearer <api-key>" alternative and instead
document the correct HMAC signing flow using the X-LD-Signature header (explain
how the webhook secret is used to compute/verify the signature), and remove or
discourage placing YOUR_API_KEY_HERE in the query string of the example URL for
integrations/generic/launchdarkly unless the integration actually
requires/query-param auth—if the backend supports X-LD-Signature verification,
replace the query-key example with instructions to configure the webhook secret
and how Robusta verifies X-LD-Signature.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: c262dc66-2753-4769-8ce2-112c69f5c546
📒 Files selected for processing (54)
docs/_ext/region_box.pydocs/_static/custom.cssdocs/_static/region-selector.jsdocs/configuration/alertmanager-integration/coralogix_managed_prometheus.rstdocs/configuration/alertmanager-integration/dynatrace.rstdocs/configuration/alertmanager-integration/gcp-monitoring.rstdocs/configuration/alertmanager-integration/google-managed-alertmanager.rstdocs/configuration/alertmanager-integration/grafana-self-hosted.rstdocs/configuration/alertmanager-integration/launchdarkly.rstdocs/configuration/alertmanager-integration/nagios.rstdocs/configuration/alertmanager-integration/newrelic.rstdocs/configuration/alertmanager-integration/outofcluster-prometheus.rstdocs/configuration/alertmanager-integration/pagerduty-alerting.rstdocs/configuration/alertmanager-integration/solarwinds.rstdocs/configuration/exporting/alert-export-api.rstdocs/configuration/exporting/alert-statistics-api.rstdocs/configuration/exporting/configuration-changes-api.rstdocs/configuration/exporting/custom-webhooks.rstdocs/configuration/exporting/namespace-resources-api.rstdocs/configuration/exporting/prometheus-query-api.rstdocs/configuration/exporting/rbac-api.rstdocs/configuration/exporting/robusta-pro-features.rstdocs/configuration/exporting/send-events-api.rstdocs/configuration/exporting/send-events/alertmanager.rstdocs/configuration/exporting/send-events/aws-cloudwatch.rstdocs/configuration/exporting/send-events/azure-monitor.rstdocs/configuration/exporting/send-events/datadog.rstdocs/configuration/exporting/send-events/dynatrace.rstdocs/configuration/exporting/send-events/gcp-monitoring.rstdocs/configuration/exporting/send-events/grafana.rstdocs/configuration/exporting/send-events/nagios.rstdocs/configuration/exporting/send-events/newrelic.rstdocs/configuration/exporting/send-events/opsgenie.rstdocs/configuration/exporting/send-events/pagerduty.rstdocs/configuration/exporting/send-events/sentry.rstdocs/configuration/exporting/send-events/solarwinds.rstdocs/configuration/exporting/send-events/splunk.rstdocs/configuration/github-actions/holmes-pr-review.rstdocs/configuration/holmesgpt/holmes-chat-api.rstdocs/configuration/metric-providers-external.rstdocs/configuration/resource-recommender.rstdocs/configuration/sinks/RobustaUI.rstdocs/configuration/sinks/slack.rstdocs/configuration/sinks/webhook.rstdocs/help.rstdocs/how-it-works/architecture.rstdocs/how-it-works/oss-vs-saas.rstdocs/index.rstdocs/notification-routing/routing-with-scopes.rstdocs/playbook-reference/index.rstdocs/playbook-reference/triggers/elasticsearch.rstdocs/setup-robusta/installation/_see_robusta_in_action-2.rstdocs/setup-robusta/installation/index.rstdocs/setup-robusta/proxies.rst
✅ Files skipped from review due to trivial changes (43)
- docs/configuration/exporting/send-events/datadog.rst
- docs/configuration/exporting/send-events/dynatrace.rst
- docs/configuration/exporting/send-events/grafana.rst
- docs/configuration/exporting/send-events/opsgenie.rst
- docs/configuration/exporting/configuration-changes-api.rst
- docs/setup-robusta/installation/index.rst
- docs/configuration/alertmanager-integration/google-managed-alertmanager.rst
- docs/playbook-reference/triggers/elasticsearch.rst
- docs/configuration/exporting/robusta-pro-features.rst
- docs/configuration/alertmanager-integration/nagios.rst
- docs/configuration/alertmanager-integration/newrelic.rst
- docs/setup-robusta/installation/_see_robusta_in_action-2.rst
- docs/how-it-works/architecture.rst
- docs/index.rst
- docs/configuration/alertmanager-integration/outofcluster-prometheus.rst
- docs/configuration/sinks/slack.rst
- docs/configuration/alertmanager-integration/dynatrace.rst
- docs/configuration/exporting/send-events-api.rst
- docs/playbook-reference/index.rst
- docs/configuration/exporting/send-events/solarwinds.rst
- docs/configuration/exporting/alert-export-api.rst
- docs/configuration/exporting/send-events/aws-cloudwatch.rst
- docs/configuration/alertmanager-integration/grafana-self-hosted.rst
- docs/configuration/holmesgpt/holmes-chat-api.rst
- docs/configuration/alertmanager-integration/gcp-monitoring.rst
- docs/configuration/github-actions/holmes-pr-review.rst
- docs/configuration/exporting/send-events/nagios.rst
- docs/notification-routing/routing-with-scopes.rst
- docs/configuration/exporting/send-events/azure-monitor.rst
- docs/how-it-works/oss-vs-saas.rst
- docs/configuration/exporting/send-events/alertmanager.rst
- docs/configuration/resource-recommender.rst
- docs/configuration/exporting/send-events/sentry.rst
- docs/configuration/exporting/send-events/gcp-monitoring.rst
- docs/setup-robusta/proxies.rst
- docs/configuration/exporting/send-events/pagerduty.rst
- docs/configuration/exporting/prometheus-query-api.rst
- docs/configuration/sinks/RobustaUI.rst
- docs/configuration/exporting/send-events/newrelic.rst
- docs/configuration/exporting/alert-statistics-api.rst
- docs/configuration/exporting/namespace-resources-api.rst
- docs/configuration/exporting/custom-webhooks.rst
- docs/configuration/alertmanager-integration/pagerduty-alerting.rst
region-selector.js: the inline dropdown was mouse-only — every option
had tabIndex=-1 and only a click handler. Add a standard listbox
keyboard contract:
- Tab focuses the trigger; ArrowDown / Enter / Space opens the menu
and focuses the active option (ArrowUp opens at the last option)
- Inside the open menu: ArrowDown/ArrowUp move focus, Home/End jump
to first/last, Enter/Space select, Escape closes and returns
focus to the trigger, Tab closes and lets the browser advance
- Roving tabindex: the focused option holds tabIndex=0, the rest -1,
so the menu participates correctly in the page's tab order
- Selection closes the menu, restores focus to the trigger, and
flows through the existing syncAll() so every other region
selector updates in lock-step
region_box.py: defensive fallback in robusta_url_role so an
empty-after-strip label can never produce an empty anchor — if the
label half ends up blank, fall back to the URL as link text. Today's
regex can't actually emit an empty label, but the guard makes a
future regex tweak safe.
Summary
This PR adds a region selector widget to the documentation that allows users to dynamically switch between US, EU, and AP region URLs throughout the page. The selector persists the user's choice in localStorage and automatically rewrites all relevant URLs when a region is selected.
Key Changes
New JavaScript module (
docs/_static/region-selector.js): Implements region detection and URL rewriting functionalityStyling (
docs/custom.css): Added comprehensive CSS for the region selector widgetConfiguration (
docs/conf.py): Registered the new JavaScript module to load on all documentation pagesImplementation Details
TreeWalkerAPI for efficient DOM traversal and text node collection.eu,.apinfixes)https://claude.ai/code/session_01N91JLFHPrBhazqZ58BEZ7y