From e7fbc4be0d1b31775c497c4e398183ae7d722e2f Mon Sep 17 00:00:00 2001 From: carlosabadia Date: Mon, 21 Sep 2026 12:41:00 +0200 Subject: [PATCH 1/2] ENG-12577: Use canonical public docs URLs in breadcrumbs --- .../news/+public-breadcrumb-urls.bugfix.md | 1 + .../reflex_docs/templates/docpage/docpage.py | 3 +- docs/app/tests/test_breadcrumbs.py | 8 ++-- docs/app/tests/test_published_seo.py | 37 ++++++++++++++++++- docs/app/tests/test_sidebar.py | 9 +++++ .../+auth-navigation-destination.bugfix.md | 1 + .../views/sidebar/__init__.py | 2 +- 7 files changed, 53 insertions(+), 8 deletions(-) create mode 100644 docs/app/news/+public-breadcrumb-urls.bugfix.md create mode 100644 packages/reflex-site-shared/news/+auth-navigation-destination.bugfix.md diff --git a/docs/app/news/+public-breadcrumb-urls.bugfix.md b/docs/app/news/+public-breadcrumb-urls.bugfix.md new file mode 100644 index 00000000000..1d3a1b6e300 --- /dev/null +++ b/docs/app/news/+public-breadcrumb-urls.bugfix.md @@ -0,0 +1 @@ +Use the public documentation URLs in breadcrumb structured data, matching each page's canonical URL. diff --git a/docs/app/reflex_docs/templates/docpage/docpage.py b/docs/app/reflex_docs/templates/docpage/docpage.py index 7f888a678f5..6be438bfc8d 100644 --- a/docs/app/reflex_docs/templates/docpage/docpage.py +++ b/docs/app/reflex_docs/templates/docpage/docpage.py @@ -28,7 +28,6 @@ from reflex_site_shared.route import Route, get_path from reflex_site_shared.templates.docs import docs_layout_shell from reflex_site_shared.utils.docpage import right_sidebar_item_highlight -from reflex_site_shared.utils.url import public_url _REGISTERED_DOC_ROUTES: set[str] = set() @@ -197,7 +196,7 @@ def breadcrumb_data(path: str, title: str) -> dict: Returns: A schema.org BreadcrumbList with canonical public URLs. """ - base = public_url() + base = DOCS_PROD_BASE canonical = base + _normalize_doc_route(path) items = [ { diff --git a/docs/app/tests/test_breadcrumbs.py b/docs/app/tests/test_breadcrumbs.py index 7776b30e16f..376e5aa0ba5 100644 --- a/docs/app/tests/test_breadcrumbs.py +++ b/docs/app/tests/test_breadcrumbs.py @@ -68,19 +68,19 @@ def test_resolve_breadcrumb_href_returns_none_for_missing_route(): "deploy_url,frontend_path,base", [ ("https://reflex.dev", "/docs", "https://reflex.dev/docs"), - ("http://localhost:3000", "/docs", "http://localhost:3000/docs"), + ("http://localhost:3000", "/docs", "https://reflex.dev/docs"), ( "https://staging.example.com/", "/preview/docs/", - "https://staging.example.com/preview/docs", + "https://reflex.dev/docs", ), - ("https://docs.example.com/", "", "https://docs.example.com"), + ("https://docs.example.com/", "", "https://reflex.dev/docs"), ], ) def test_structured_breadcrumbs_use_real_canonical_routes( monkeypatch, deploy_url, frontend_path, base ): - """Structured navigation names existing pages and includes the docs root.""" + """Structured navigation matches public canonicals, independent of deployment.""" docpage_module = importlib.import_module("reflex_docs.templates.docpage.docpage") monkeypatch.setattr( "reflex_site_shared.utils.url.get_config", diff --git a/docs/app/tests/test_published_seo.py b/docs/app/tests/test_published_seo.py index 634a8ff8d8e..5bfce73c457 100644 --- a/docs/app/tests/test_published_seo.py +++ b/docs/app/tests/test_published_seo.py @@ -1,5 +1,6 @@ """Validate URL metadata in the actual production build, not only its inputs.""" +import json from html.parser import HTMLParser from pathlib import Path from urllib.parse import urljoin, urlsplit @@ -14,7 +15,7 @@ class PageURLs(HTMLParser): - """Collect canonical and social URLs from generated HTML.""" + """Collect canonical, social and structured metadata from generated HTML.""" def __init__(self, html: str): super().__init__() @@ -22,11 +23,15 @@ def __init__(self, html: str): self.social = [] self.markdown = [] self.links = [] + self.structured_data = [] + self._json_ld = None self.feed(html) def handle_starttag(self, tag, attrs): """Collect URL-bearing metadata tags.""" attrs = dict(attrs) + if tag == "script" and attrs.get("type") == "application/ld+json": + self._json_ld = [] if tag == "link" and attrs.get("rel") == "canonical": self.canonical.append(attrs.get("href")) if ( @@ -43,6 +48,34 @@ def handle_starttag(self, tag, attrs): ): self.social.append(attrs.get("content")) + def handle_data(self, data): + """Collect script text without decoding JSON until the closing tag.""" + if self._json_ld is not None: + self._json_ld.append(data) + + def handle_endtag(self, tag): + """Parse complete structured-data scripts.""" + if tag == "script" and self._json_ld is not None: + self.structured_data.append(json.loads("".join(self._json_ld))) + self._json_ld = None + + +def check_breadcrumbs(metadata, url, canonical_urls): + """Validate ordered breadcrumbs against the exported public page inventory.""" + breadcrumbs = [ + data + for data in metadata.structured_data + if data.get("@type") == "BreadcrumbList" + ] + assert len(breadcrumbs) == 1, (url, breadcrumbs) + items = breadcrumbs[0]["itemListElement"] + assert items, url + assert [item["position"] for item in items] == list(range(1, len(items) + 1)) + assert items[-1]["item"] == url + for item in items: + assert item["@type"] == "ListItem" and item["name"], (url, item) + assert item["item"] in canonical_urls, (url, item) + @pytest.mark.xfail( not SITEMAP.is_file(), @@ -59,6 +92,7 @@ def test_generated_sitemap_and_page_urls_share_public_origin(): ] assert urls assert len(urls) == len(set(urls)) + canonical_urls = set(urls) canonical_paths = {urlsplit(url).path for url in urls} redirected = set() for url in urls: @@ -70,6 +104,7 @@ def test_generated_sitemap_and_page_urls_share_public_origin(): assert page.is_file(), page metadata = PageURLs(page.read_text()) assert metadata.canonical == [url], (url, metadata.canonical) + check_breadcrumbs(metadata, url, canonical_urls) assert len(metadata.social) == 2, (url, metadata.social) assert all(value == url for value in metadata.social), (url, metadata.social) markdown_url = ( diff --git a/docs/app/tests/test_sidebar.py b/docs/app/tests/test_sidebar.py index b13d4fb258d..917b2583c24 100644 --- a/docs/app/tests/test_sidebar.py +++ b/docs/app/tests/test_sidebar.py @@ -3,6 +3,15 @@ import pytest +def test_mobile_navigation_auth_links_directly_to_enterprise_docs(): + """The shared mobile menu must not send readers through the legacy alias.""" + from reflex_site_shared.views.sidebar import solutions_panel + + rendered = str(solutions_panel()) + assert "/docs/enterprise/auth/overview/" in rendered + assert "/docs/authentication/authentication-overview/" not in rendered + + @pytest.mark.parametrize("label", ["APIs", "URLs"]) def test_ai_integration_group_and_page_use_matching_acronyms(label): """Keep plural acronyms consistent between sidebar groups and their pages.""" diff --git a/packages/reflex-site-shared/news/+auth-navigation-destination.bugfix.md b/packages/reflex-site-shared/news/+auth-navigation-destination.bugfix.md new file mode 100644 index 00000000000..e7bb0b4df23 --- /dev/null +++ b/packages/reflex-site-shared/news/+auth-navigation-destination.bugfix.md @@ -0,0 +1 @@ +Link the mobile navigation's Auth entry directly to the current authentication documentation. diff --git a/packages/reflex-site-shared/src/reflex_site_shared/views/sidebar/__init__.py b/packages/reflex-site-shared/src/reflex_site_shared/views/sidebar/__init__.py index a43bd73ec96..91d5cae7965 100644 --- a/packages/reflex-site-shared/src/reflex_site_shared/views/sidebar/__init__.py +++ b/packages/reflex-site-shared/src/reflex_site_shared/views/sidebar/__init__.py @@ -327,7 +327,7 @@ def solutions_panel() -> rx.Component: drawer_panel_item( nav_icon("LoginMethodIcon"), "Auth", - "/docs/authentication/authentication-overview/", + "/docs/enterprise/auth/overview/", ), drawer_panel_item( nav_icon("UserUnlock01Icon"), From 572f5b44b6813ab777d22fc7312083a67726b509 Mon Sep 17 00:00:00 2001 From: carlosabadia Date: Mon, 21 Sep 2026 12:44:38 +0200 Subject: [PATCH 2/2] Remove docs breadcrumb news fragment --- docs/app/news/+public-breadcrumb-urls.bugfix.md | 1 - 1 file changed, 1 deletion(-) delete mode 100644 docs/app/news/+public-breadcrumb-urls.bugfix.md diff --git a/docs/app/news/+public-breadcrumb-urls.bugfix.md b/docs/app/news/+public-breadcrumb-urls.bugfix.md deleted file mode 100644 index 1d3a1b6e300..00000000000 --- a/docs/app/news/+public-breadcrumb-urls.bugfix.md +++ /dev/null @@ -1 +0,0 @@ -Use the public documentation URLs in breadcrumb structured data, matching each page's canonical URL.