diff --git a/src/govoplan_docs/backend/api/v1/routes.py b/src/govoplan_docs/backend/api/v1/routes.py index aaccfd7..c59d9c9 100644 --- a/src/govoplan_docs/backend/api/v1/routes.py +++ b/src/govoplan_docs/backend/api/v1/routes.py @@ -608,7 +608,7 @@ def _documentation_topic_payload( def _user_topic_metadata(kind: str, metadata: Mapping[str, Any]) -> dict[str, Any]: projected: dict[str, Any] = {} scalar_keys = ("kind", "screen", "section", "outcome", "result", "verification", "purpose", "when_used", "user_explanation") - list_keys = ("prerequisites", "steps", "current_configuration", "limitations", "related_topic_ids") + list_keys = ("prerequisites", "steps", "current_configuration", "limitations", "related_topic_ids", "help_contexts") id_keys = ("tree_parent_id", "parent_topic_id", "parent_id") for key in scalar_keys: value = _bounded_string(metadata.get(key), maximum=2_000) diff --git a/tests/test_docs_context.py b/tests/test_docs_context.py index e889dae..1528955 100644 --- a/tests/test_docs_context.py +++ b/tests/test_docs_context.py @@ -110,6 +110,7 @@ class DocsContextTests(unittest.TestCase): "kind": "workflow", "screen": "Example", "steps": ["Do the safe thing."], + "help_contexts": ["example.list"], "current_configuration": ["The bounded option is enabled."], "constraints": [{ "id": "domain", @@ -141,6 +142,7 @@ class DocsContextTests(unittest.TestCase): self.assertEqual(topic["blockers"], {"modules": [], "capabilities": [], "scopes": []}) self.assertNotIn("raw_policy", topic["metadata"]) self.assertNotIn("api_path", topic["metadata"]) + self.assertEqual(topic["metadata"]["help_contexts"], ["example.list"]) self.assertEqual(topic["metadata"]["constraints"][0], { "id": "domain", "label": "Domain", diff --git a/webui/src/features/docs/DocsPage.tsx b/webui/src/features/docs/DocsPage.tsx index 1094dc7..1437e07 100644 --- a/webui/src/features/docs/DocsPage.tsx +++ b/webui/src/features/docs/DocsPage.tsx @@ -794,8 +794,25 @@ function uniqueTopics(topics: DocsDocumentationTopic[]): DocsDocumentationTopic[ function selectedPageFromSearch(search: string, pages: DocsPageNode[]): DocsPageNode | null { if (!pages.length) return null; - const requested = new URLSearchParams(search).get("topic") || ""; - return pages.find((page) => page.id === requested) ?? pages[0]; + const params = new URLSearchParams(search); + const requested = params.get("topic") || ""; + if (requested) return pages.find((page) => page.id === requested) ?? pages[0]; + const helpContext = params.get("context") || ""; + if (helpContext) { + const exact = pages.find((page) => page.kind === "topic" && metadataList(page.topic.metadata, "help_contexts").includes(helpContext)); + if (exact) return exact; + const moduleId = helpContextModuleId(helpContext); + const moduleTopic = pages.find((page) => page.kind === "topic" && page.topic.source_module_id === moduleId); + if (moduleTopic) return moduleTopic; + } + return pages[0]; +} + +function helpContextModuleId(value: string): string { + const prefix = value.split(".", 1)[0]; + if (prefix === "campaign") return "campaigns"; + if (prefix === "address-book") return "addresses"; + return prefix; } function outlineForPage(page: DocsPageNode | null, showTechnical: boolean): OutlineItem[] {