From 581467e95cf618030d1d0200673e65a27dd716a0 Mon Sep 17 00:00:00 2001
From: Sarah Boyce <42296566+sarahboyce@users.noreply.github.com>
Date: Fri, 7 Feb 2025 11:32:19 +0100
Subject: [PATCH] Added documentation type filtering to search.
---
djangoproject/scss/_style.scss | 36 +++++++++++++
docs/templates/docs/search_results.html | 7 +++
docs/tests.py | 67 +++++++++++++++++++++++++
docs/views.py | 5 ++
4 files changed, 115 insertions(+)
diff --git a/djangoproject/scss/_style.scss b/djangoproject/scss/_style.scss
index 48b436a08..daf9ad799 100644
--- a/djangoproject/scss/_style.scss
+++ b/djangoproject/scss/_style.scss
@@ -2563,6 +2563,42 @@ table.docutils th {
}
}
+.tabs {
+ display: flex;
+ gap: 10px;
+ border-bottom: 2px solid var(--hairline-color);
+ overflow-x: auto;
+ white-space: nowrap;
+ padding-bottom: 0;
+ position: relative;
+
+ a:not(.active):focus,
+ a:not(.active):active,
+ a:not(.active):hover {
+ outline: none;
+ border-bottom: 3px solid var(--hairline-color);
+ }
+
+ .tab {
+ padding: 10px 20px;
+ text-decoration: none;
+ background: transparent;
+ border: none;
+ border-bottom: 3px solid transparent;
+ transition: color 0.3s ease, border-bottom 0.3s ease;
+ font-size: 16px;
+ color: var(--text-light);
+ flex-shrink: 0;
+ }
+
+ .active {
+ color: var(--body-fg);
+ font-weight: bold;
+ border-bottom: 3px solid var(--primary);
+ }
+}
+
+
.search-links {
@extend .list-links;
diff --git a/docs/templates/docs/search_results.html b/docs/templates/docs/search_results.html
index 11daede6c..5eaf95bd6 100644
--- a/docs/templates/docs/search_results.html
+++ b/docs/templates/docs/search_results.html
@@ -32,6 +32,13 @@
{% trans "No search query given" %}
{% if query %}
+
{% for result in page.object_list %}
-
diff --git a/docs/tests.py b/docs/tests.py
index f4ae89910..ad3cb537f 100644
--- a/docs/tests.py
+++ b/docs/tests.py
@@ -195,6 +195,73 @@ def test_empty_get(self):
)
self.assertEqual(response.status_code, 200)
+ def test_search_type_filter(self):
+ release = Release.objects.create(version="5.1")
+ doc_release = DocumentRelease.objects.create(release=release)
+ types = [
+ ("topics", "Using Django"),
+ ("releases", "Release notes"),
+ ("howto", "How-to guides"),
+ ("ref", "API Reference"),
+ ]
+
+ for path, title in types:
+ Document.objects.create(
+ **{
+ "metadata": {
+ "body": "Generic Views",
+ "breadcrumbs": [
+ {"path": path, "title": title},
+ ],
+ "parents": path,
+ "slug": "generic-views",
+ "title": "Generic views",
+ "toc": '\n',
+ },
+ "path": f"{path}/generic-views",
+ "release": doc_release,
+ "title": "Generic views",
+ }
+ )
+
+ for path, title in types:
+ with self.subTest(type=path):
+ response = self.client.get(
+ f"/en/5.1/search/?q=generic&type={path}",
+ headers={"host": "docs.djangoproject.localhost:8000"},
+ )
+ self.assertEqual(response.status_code, 200)
+ self.assertContains(
+ response,
+ "Only 1 result for generic in version 5.1",
+ html=True,
+ )
+ self.assertContains(response, "tab active", count=1)
+ self.assertContains(
+ response,
+ f'{title}',
+ html=True,
+ )
+ self.assertContains(
+ response, 'All', html=True
+ )
+
+ with self.subTest(type="all"):
+ response = self.client.get(
+ "/en/5.1/search/?q=generic",
+ headers={"host": "docs.djangoproject.localhost:8000"},
+ )
+ self.assertEqual(response.status_code, 200)
+ self.assertContains(
+ response, "4 results for generic in version 5.1", html=True
+ )
+ self.assertContains(response, "tab active", count=1)
+ self.assertContains(
+ response,
+ 'All',
+ html=True,
+ )
+
class TemplateTagTests(TestCase):
fixtures = ["doc_test_fixtures"]
diff --git a/docs/views.py b/docs/views.py
index 68cccbcfe..ca3ae1e6f 100644
--- a/docs/views.py
+++ b/docs/views.py
@@ -189,6 +189,10 @@ def search_results(request, lang, version, per_page=10, orphans=3):
return redirect(exact)
results = Document.objects.search(q, release)
+ type_value = request.GET.get("type", None)
+ filtered_by_type = type_value in ["ref", "releases", "howto", "topics"]
+ if filtered_by_type:
+ results = results.filter(metadata__parents__startswith=type_value)
page_number = request.GET.get("page") or 1
paginator = Paginator(results, per_page=per_page, orphans=orphans)
@@ -217,6 +221,7 @@ def search_results(request, lang, version, per_page=10, orphans=3):
"page": page,
"paginator": paginator,
"start_sel": START_SEL,
+ "type": type_value if filtered_by_type else None,
}
)