feat: improve Wagtail admin editor experience for articles #40

Merged
mark merged 6 commits from feature/improve-editor-experience into main 2026-03-03 20:46:31 +00:00
5 changed files with 78 additions and 3 deletions
Showing only changes of commit b897447296 - Show all commits

View File

@@ -15,6 +15,7 @@ from wagtail.admin.panels import FieldPanel, ObjectList, PageChooserPanel, Tabbe
from wagtail.contrib.routable_page.models import RoutablePageMixin, route from wagtail.contrib.routable_page.models import RoutablePageMixin, route
from wagtail.fields import RichTextField, StreamField from wagtail.fields import RichTextField, StreamField
from wagtail.models import Page from wagtail.models import Page
from wagtail.search import index
from wagtailseo.models import SeoMixin from wagtailseo.models import SeoMixin
from apps.blog.blocks import ARTICLE_BODY_BLOCKS from apps.blog.blocks import ARTICLE_BODY_BLOCKS
@@ -232,7 +233,9 @@ class ArticlePage(SeoMixin, Page):
] ]
) )
search_fields = Page.search_fields search_fields = Page.search_fields + [
index.SearchField("summary"),
]
def save(self, *args: Any, **kwargs: Any) -> None: def save(self, *args: Any, **kwargs: Any) -> None:
if not self.category_id: if not self.category_id:

View File

@@ -232,3 +232,44 @@ def test_article_edit_page_has_tabbed_interface(client, django_user_model, home_
assert "Metadata" in content assert "Metadata" in content
assert "Publishing" in content assert "Publishing" in content
assert "SEO" in content assert "SEO" in content
@pytest.mark.django_db
@override_settings(ALLOWED_HOSTS=["testserver", "localhost", "127.0.0.1"])
def test_articles_listing_has_status_filter(client, django_user_model, home_page):
"""The Articles listing should accept status filter parameter."""
admin = django_user_model.objects.create_superuser(
username="admin", email="admin@example.com", password="admin-pass"
)
client.force_login(admin)
response = client.get("/cms/articles/?status=live")
assert response.status_code == 200
@pytest.mark.django_db
@override_settings(ALLOWED_HOSTS=["testserver", "localhost", "127.0.0.1"])
def test_articles_listing_has_tag_filter(client, django_user_model, home_page):
"""The Articles listing should accept tag filter parameter."""
admin = django_user_model.objects.create_superuser(
username="admin", email="admin@example.com", password="admin-pass"
)
client.force_login(admin)
response = client.get("/cms/articles/?tag=1")
assert response.status_code == 200
@pytest.mark.django_db
def test_article_listing_default_ordering():
"""ArticlePageListingViewSet should default to -published_date ordering."""
from apps.blog.wagtail_hooks import ArticlePageListingViewSet
assert ArticlePageListingViewSet.default_ordering == "-published_date"
@pytest.mark.django_db
def test_article_search_fields_include_summary():
"""ArticlePage.search_fields should index the summary field."""
field_names = [
f.field_name for f in ArticlePage.search_fields if hasattr(f, "field_name")
]
assert "summary" in field_names

View File

@@ -1,4 +1,5 @@
import django_filters import django_filters
from taggit.models import Tag
from wagtail import hooks from wagtail import hooks
from wagtail.admin.filters import WagtailFilterSet from wagtail.admin.filters import WagtailFilterSet
from wagtail.admin.ui.components import Component from wagtail.admin.ui.components import Component
@@ -11,6 +12,12 @@ from wagtail.snippets.views.snippets import SnippetViewSet
from apps.authors.models import Author from apps.authors.models import Author
from apps.blog.models import ArticlePage, Category, TagMetadata from apps.blog.models import ArticlePage, Category, TagMetadata
STATUS_CHOICES = [
("live", "Published"),
("draft", "Draft"),
("scheduled", "Scheduled"),
]
class TagMetadataViewSet(SnippetViewSet): class TagMetadataViewSet(SnippetViewSet):
model = TagMetadata model = TagMetadata
@@ -35,6 +42,17 @@ register_snippet(CategoryViewSet)
# ── Articles page listing ──────────────────────────────────────────────────── # ── Articles page listing ────────────────────────────────────────────────────
class StatusFilter(django_filters.ChoiceFilter):
def filter(self, qs, value): # noqa: A003
if value == "live":
return qs.filter(live=True)
if value == "draft":
return qs.filter(live=False, go_live_at__isnull=True)
if value == "scheduled":
return qs.filter(live=False, go_live_at__isnull=False)
return qs
class ArticleFilterSet(WagtailFilterSet): class ArticleFilterSet(WagtailFilterSet):
category = django_filters.ModelChoiceFilter( category = django_filters.ModelChoiceFilter(
queryset=Category.objects.all(), queryset=Category.objects.all(),
@@ -44,6 +62,15 @@ class ArticleFilterSet(WagtailFilterSet):
queryset=Author.objects.all(), queryset=Author.objects.all(),
empty_label="All authors", empty_label="All authors",
) )
status = StatusFilter(
choices=STATUS_CHOICES,
empty_label="All statuses",
)
tag = django_filters.ModelChoiceFilter(
field_name="tags",
queryset=Tag.objects.all(),
empty_label="All tags",
)
class Meta: class Meta:
model = ArticlePage model = ArticlePage
@@ -66,6 +93,7 @@ class ArticlePageListingViewSet(PageListingViewSet):
PageStatusColumn("status", sort_key="live"), PageStatusColumn("status", sort_key="live"),
] ]
filterset_class = ArticleFilterSet filterset_class = ArticleFilterSet
default_ordering = "-published_date"
@hooks.register("register_admin_viewset") @hooks.register("register_admin_viewset")

View File

@@ -1,5 +1,7 @@
from __future__ import annotations from __future__ import annotations
import os
from django.contrib.auth import get_user_model from django.contrib.auth import get_user_model
from django.core.management.base import BaseCommand from django.core.management.base import BaseCommand
from taggit.models import Tag from taggit.models import Tag
@@ -200,8 +202,8 @@ class Command(BaseCommand):
] ]
) )
# Admin user for E2E admin tests # Admin user for E2E admin tests — only when E2E_MODE is set
if not User.objects.filter(username="e2e-admin").exists(): if os.environ.get("E2E_MODE") and not User.objects.filter(username="e2e-admin").exists():
User.objects.create_superuser( User.objects.create_superuser(
username="e2e-admin", username="e2e-admin",
email="e2e-admin@example.com", email="e2e-admin@example.com",

View File

@@ -24,6 +24,7 @@ services:
EMAIL_BACKEND: django.core.mail.backends.console.EmailBackend EMAIL_BACKEND: django.core.mail.backends.console.EmailBackend
DEFAULT_FROM_EMAIL: hello@nohypeai.com DEFAULT_FROM_EMAIL: hello@nohypeai.com
NEWSLETTER_PROVIDER: buttondown NEWSLETTER_PROVIDER: buttondown
E2E_MODE: "1"
depends_on: depends_on:
db: db:
condition: service_healthy condition: service_healthy