fix: resolve review round 2, E2E failures, and mypy error
Review blocker A — form error swap and false success: - Change HTMX contract so forms target their own container (outerHTML) instead of appending to #comments-list - Use OOB swaps to append approved comments to the correct target - Add success/error message display inside form templates - Remove hx-on::after-request handlers (no longer needed) Review blocker B — reply rendering shape: - Create _reply.html partial with compact reply markup - Approved replies via HTMX now use compact template + OOB swap into parent's .replies-container - Reply form errors render inside reply form container E2E test fixes: - Update 4 failing tests to wait for inline HTMX messages instead of redirect-based URL assertions - Add aria-label='Comment form errors' to form error display - Rename test_reply_submission_redirects to test_reply_submission_shows_moderation_message Mypy internal error workaround: - Add mypy override for apps.comments.views (django-stubs triggers internal error on ORM annotate() chain with mypy 1.11.2) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -152,13 +152,6 @@
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if comment_form and comment_form.errors %}
|
||||
<div aria-label="Comment form errors" class="mb-6 p-4 bg-red-50 dark:bg-red-900/20 border border-red-200 dark:border-red-800 font-mono text-sm text-red-600 dark:text-red-400">
|
||||
{{ comment_form.non_field_errors }}
|
||||
{% for field in comment_form %}{{ field.errors }}{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% include "comments/_comment_form.html" %}
|
||||
</section>
|
||||
{% endif %}
|
||||
|
||||
@@ -1,9 +1,19 @@
|
||||
{% load static %}
|
||||
<div id="comment-form-container" class="bg-brand-surfaceLight dark:bg-brand-surfaceDark border border-zinc-200 dark:border-zinc-800 p-6">
|
||||
<h3 class="font-display font-bold text-xl mb-6">Post a Comment</h3>
|
||||
{% if success_message %}
|
||||
<div class="mb-4 p-3 font-mono text-sm bg-brand-cyan/10 text-brand-cyan border border-brand-cyan/20">
|
||||
{{ success_message }}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if comment_form.errors %}
|
||||
<div aria-label="Comment form errors" class="mb-4 p-3 font-mono text-sm bg-red-50 dark:bg-red-900/20 text-red-600 dark:text-red-400 border border-red-200 dark:border-red-800">
|
||||
{% for error in comment_form.non_field_errors %}<p>{{ error }}</p>{% endfor %}
|
||||
{% for field in comment_form %}{% for error in field.errors %}<p>{{ field.label }}: {{ error }}</p>{% endfor %}{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
<form method="post" action="{% url 'comment_post' %}" data-comment-form class="space-y-4"
|
||||
hx-post="{% url 'comment_post' %}" hx-target="#comments-list" hx-swap="beforeend"
|
||||
hx-on::after-request="if(event.detail.successful) { this.reset(); document.getElementById('comment-success')?.remove(); this.insertAdjacentHTML('beforebegin', '<div id="comment-success" class="mb-4 p-3 font-mono text-sm bg-brand-cyan/10 text-brand-cyan border border-brand-cyan/20">Comment posted!</div>'); }">
|
||||
hx-post="{% url 'comment_post' %}" hx-target="#comment-form-container" hx-swap="outerHTML">
|
||||
{% csrf_token %}
|
||||
<input type="hidden" name="article_id" value="{{ page.id }}" />
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
|
||||
10
templates/comments/_reply.html
Normal file
10
templates/comments/_reply.html
Normal file
@@ -0,0 +1,10 @@
|
||||
<article id="comment-{{ comment.id }}" class="mt-6 ml-8 bg-zinc-50 dark:bg-zinc-900 border border-zinc-200 dark:border-zinc-800 border-l-2 border-l-brand-cyan p-4">
|
||||
<div class="flex items-center gap-3 mb-2">
|
||||
<div class="w-7 h-7 bg-gradient-to-tr from-brand-pink to-brand-cyan shrink-0"></div>
|
||||
<div>
|
||||
<div class="font-display font-bold text-sm">{{ comment.author_name }}</div>
|
||||
<div class="font-mono text-xs text-zinc-500">{{ comment.created_at|date:"M j, Y" }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<p class="text-zinc-700 dark:text-zinc-300 text-sm leading-relaxed">{{ comment.body }}</p>
|
||||
</article>
|
||||
@@ -1,20 +1,30 @@
|
||||
{% load static %}
|
||||
<form method="post" action="{% url 'comment_post' %}" class="mt-4 pt-4 border-t border-zinc-100 dark:border-zinc-800"
|
||||
hx-post="{% url 'comment_post' %}" hx-target="#comments-list" hx-swap="beforeend" hx-on::after-request="if(event.detail.successful) this.reset()">
|
||||
{% csrf_token %}
|
||||
<input type="hidden" name="article_id" value="{{ page.id }}" />
|
||||
<input type="hidden" name="parent_id" value="{{ comment.id }}" />
|
||||
<div class="flex gap-3 mb-3">
|
||||
<input type="text" name="author_name" required placeholder="Your name"
|
||||
class="flex-1 bg-transparent border border-zinc-300 dark:border-zinc-700 px-3 py-2 font-mono text-sm focus:outline-none focus:border-brand-pink focus:shadow-neon-pink transition-colors" />
|
||||
<input type="email" name="author_email" required placeholder="your@email.com"
|
||||
class="flex-1 bg-transparent border border-zinc-300 dark:border-zinc-700 px-3 py-2 font-mono text-sm focus:outline-none focus:border-brand-pink focus:shadow-neon-pink transition-colors" />
|
||||
</div>
|
||||
<textarea name="body" required placeholder="Write a reply..." rows="2"
|
||||
class="w-full bg-transparent border border-zinc-300 dark:border-zinc-700 px-3 py-2 font-mono text-sm focus:outline-none focus:border-brand-pink focus:shadow-neon-pink transition-colors mb-3 resize-none"></textarea>
|
||||
<input type="text" name="honeypot" hidden />
|
||||
{% if turnstile_site_key %}
|
||||
<div class="cf-turnstile mb-3" data-sitekey="{{ turnstile_site_key }}" data-theme="auto" data-size="compact"></div>
|
||||
<div id="reply-form-container-{{ comment.id }}" class="mt-4 pt-4 border-t border-zinc-100 dark:border-zinc-800">
|
||||
{% if reply_success_message %}
|
||||
<div class="mb-3 p-2 font-mono text-sm bg-brand-cyan/10 text-brand-cyan border border-brand-cyan/20">{{ reply_success_message }}</div>
|
||||
{% endif %}
|
||||
<button type="submit" class="px-4 py-2 bg-zinc-200 dark:bg-zinc-800 font-display font-bold text-sm hover:bg-brand-pink hover:text-white transition-colors">Reply</button>
|
||||
</form>
|
||||
{% if reply_form_errors %}
|
||||
<div aria-label="Comment form errors" class="mb-3 p-2 font-mono text-sm bg-red-50 dark:bg-red-900/20 text-red-600 dark:text-red-400 border border-red-200 dark:border-red-800">
|
||||
{% for field, errors in reply_form_errors.items %}{% for error in errors %}<p>{{ error }}</p>{% endfor %}{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
<form method="post" action="{% url 'comment_post' %}"
|
||||
hx-post="{% url 'comment_post' %}" hx-target="#reply-form-container-{{ comment.id }}" hx-swap="outerHTML">
|
||||
{% csrf_token %}
|
||||
<input type="hidden" name="article_id" value="{{ page.id }}" />
|
||||
<input type="hidden" name="parent_id" value="{{ comment.id }}" />
|
||||
<div class="flex gap-3 mb-3">
|
||||
<input type="text" name="author_name" required placeholder="Your name"
|
||||
class="flex-1 bg-transparent border border-zinc-300 dark:border-zinc-700 px-3 py-2 font-mono text-sm focus:outline-none focus:border-brand-pink focus:shadow-neon-pink transition-colors" />
|
||||
<input type="email" name="author_email" required placeholder="your@email.com"
|
||||
class="flex-1 bg-transparent border border-zinc-300 dark:border-zinc-700 px-3 py-2 font-mono text-sm focus:outline-none focus:border-brand-pink focus:shadow-neon-pink transition-colors" />
|
||||
</div>
|
||||
<textarea name="body" required placeholder="Write a reply..." rows="2"
|
||||
class="w-full bg-transparent border border-zinc-300 dark:border-zinc-700 px-3 py-2 font-mono text-sm focus:outline-none focus:border-brand-pink focus:shadow-neon-pink transition-colors mb-3 resize-none"></textarea>
|
||||
<input type="text" name="honeypot" hidden />
|
||||
{% if turnstile_site_key %}
|
||||
<div class="cf-turnstile mb-3" data-sitekey="{{ turnstile_site_key }}" data-theme="auto" data-size="compact"></div>
|
||||
{% endif %}
|
||||
<button type="submit" class="px-4 py-2 bg-zinc-200 dark:bg-zinc-800 font-display font-bold text-sm hover:bg-brand-pink hover:text-white transition-colors">Reply</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user