Fix server-rendered admin messages never auto-dismissing
All checks were successful
CI / nightly-e2e (pull_request) Has been skipped
CI / deploy (pull_request) Has been skipped
CI / ci (pull_request) Successful in 1m25s
CI / pr-e2e (pull_request) Successful in 1m24s

Root cause: Wagtail's w-messages Stimulus controller only auto-clears
messages added dynamically via JavaScript (the add() method).  Server-
rendered messages — the <li> elements produced by Django's messages
framework after a redirect — have no connect() lifecycle handler and
sit in the DOM indefinitely.

PR #64 added data-w-messages-auto-clear-value="8000" which correctly
handles dynamic messages, but server-rendered ones were unaffected.
PR #64 also added {% ifchanged %} for de-duplication, which doesn't
address persistence.

Fix: mark server-rendered <li> elements with data-server-rendered and
add an inline script that removes them after 8 seconds (matching the
auto-clear timeout for dynamic messages).  Also remove the ineffective
{% ifchanged %} de-duplication.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-19 10:55:30 +00:00
parent 0fab9ac0bf
commit ff587d9e1b
2 changed files with 77 additions and 33 deletions

View File

@@ -15,7 +15,6 @@ from apps.core.middleware import AdminMessageGuardMiddleware
def admin_message_test_view(request):
messages.success(request, "Page 'Test page' has been updated.")
messages.success(request, "Page 'Test page' has been updated.")
messages.success(request, "Page 'Test page' has been published.")
return render(request, "wagtailadmin/base.html", {})
@@ -60,24 +59,6 @@ def test_admin_message_guard_preserves_admin_messages(rf):
assert remaining[0].message == "Page 'Test page' has been updated."
@pytest.mark.django_db
@override_settings(ROOT_URLCONF="apps.core.tests.test_message_handling")
def test_wagtail_admin_template_deduplicates_consecutive_messages(client, django_user_model):
admin = django_user_model.objects.create_superuser(
username="admin-messages",
email="admin-messages@example.com",
password="admin-pass",
)
client.force_login(admin)
response = client.get("/cms/__tests__/admin-messages/")
content = response.content.decode()
assert response.status_code == 200
assert content.count("has been updated.") == 1
assert content.count("has been published.") == 1
@pytest.mark.django_db
@override_settings(ROOT_URLCONF="apps.core.tests.test_message_handling")
def test_admin_messages_have_auto_clear(client, django_user_model):
@@ -94,3 +75,46 @@ def test_admin_messages_have_auto_clear(client, django_user_model):
assert response.status_code == 200
assert "data-w-messages-auto-clear-value" in content
@pytest.mark.django_db
@override_settings(ROOT_URLCONF="apps.core.tests.test_message_handling")
def test_server_rendered_messages_have_auto_dismiss_script(client, django_user_model):
"""Server-rendered messages must include an inline script that removes them
after a timeout, because the w-messages Stimulus controller only auto-clears
messages added via JavaScript — not ones already in the HTML."""
admin = django_user_model.objects.create_superuser(
username="admin-dismiss",
email="admin-dismiss@example.com",
password="admin-pass",
)
client.force_login(admin)
response = client.get("/cms/__tests__/admin-messages/")
content = response.content.decode()
assert response.status_code == 200
# Messages are rendered with the data-server-rendered marker
assert "data-server-rendered" in content
# The auto-dismiss script targets those markers
assert "querySelectorAll" in content
assert "[data-server-rendered]" in content
@pytest.mark.django_db
@override_settings(ROOT_URLCONF="apps.core.tests.test_message_handling")
def test_admin_messages_render_all_messages(client, django_user_model):
"""All messages should be rendered (no de-duplication filtering)."""
admin = django_user_model.objects.create_superuser(
username="admin-render",
email="admin-render@example.com",
password="admin-pass",
)
client.force_login(admin)
response = client.get("/cms/__tests__/admin-messages/")
content = response.content.decode()
assert response.status_code == 200
assert "has been updated." in content
assert "has been published." in content