Repository navigation
Comments: Hide zero-count status filters on the Comments list table - #14056
AbdiTolesa wants to merge 11 commits into
Conversation
WP_Posts_List_Table::get_views() hides any status filter link (other than "All") whose post count is zero. WP_Comments_List_Table::get_views() had no equivalent check, so "Pending", "Approved", "Spam", "Trash", and "Mine" were always shown even with a count of (0), cluttering the screen with filters that return no results. Apply the same logic: skip adding a status link when its count is empty, except for "All" which always stays visible. This reverses the behavior asserted by test_get_views_should_return_views_by_default (added for #42066), which is removed since it codified the opposite, now-undesired behavior. No replacement test is added in this change. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the Core Committers: Use this line as a base for the props when committing in SVN: To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook. |
Test using WordPress PlaygroundThe changes in this pull request can previewed and tested using a WordPress Playground instance. WordPress Playground is an experimental project that creates a full WordPress instance entirely within the browser. Some things to be aware of
For more details about these limitations and more, check out the Limitations page in the WordPress Playground documentation. |
Covers WP_Comments_List_Table::get_views() hiding "Pending", "Approved", "Spam", and "Trash" links when their count is zero, while keeping "All" visible, and showing a status link again once its count becomes nonzero. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…ive view Addresses review feedback on the zero-count filtering added in the previous commit. Comments is a moderation queue, not a plain list like Posts, so: - "Pending" and "Approved" now always stay visible regardless of count. A "(0)" on Pending is reassuring confirmation of an empty queue, not clutter, and hiding it could read as moderation being broken. - The currently active $comment_status is never hidden, even at zero count, so an admin who lands on e.g. ?comment_status=moderated (from a stale notification) or ?comment_status=spam (after "Empty Spam") always has a visible, "current" tab showing what they're filtered to. "Mine", "Spam", and "Trash" are still hidden when empty and not the active view. Updated the existing get_views() tests for the new rules and added a regression test for the active-view exception. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
… hiding WP_Post_Comments_List_Table extends WP_Comments_List_Table and inherits its get_views(), so it was asserting the same superseded all-statuses-always-shown behavior (also added for #42066) that was already updated in wpCommentsListTable.php. Missed in the earlier commit since it lives in a separate test file. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…n-zero via AJAX Status links (e.g. "Trash", "Spam") are now hidden entirely when their count is zero (see WP_Comments_List_Table::get_views()). edit-comments.js only updates the *text* of existing count spans after an AJAX moderation action, so when a status link was never rendered in the first place, incrementing its (non-existent) count text does nothing: trashing the first comment on a site with no prior trashed comments left no way to reach the Trash view without a full page reload. _wp_ajax_delete_comment_response() now re-renders the comment status view tabs (WP_Comments_List_Table::views()), using the requesting page's own comment_status/p context (recovered from the posted _url), and includes the fresh HTML in the AJAX response's supplemental data. edit-comments.js replaces the whole .subsubsub list with it, after the existing incremental count updates (since it supersedes them), so a status that just crossed zero appears or disappears immediately. Added a test asserting the "Trash" link is present in the AJAX response after trashing the only trashable comment; confirmed it fails without the fix and passes with it. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…r AJAX
Per review feedback, replace the previous AJAX approach to keeping
status view tabs ("Mine", "Spam", "Trash") in sync after a moderation
action. Re-rendering WP_Comments_List_Table::views() server-side and
replacing the whole .subsubsub node over AJAX (previous commit) works
in isolation, but destroys any DOM nodes, event handlers, or data a
plugin may have attached to the existing view links on every single
moderation action - an architectural regression for plugin
extensibility.
Instead:
- WP_Comments_List_Table::get_views() no longer omits empty, non-active
status entries from its returned array; every status key is always
present now, and get_views() instead records which ones are empty in
a new $empty_views property.
- A new WP_Comments_List_Table::views() override (the base
WP_List_Table::views() doesn't support this) adds an `is-empty-view`
class to those views' <li> elements, hidden via a new CSS rule,
rather than omitting their markup.
- edit-comments.js's delAfter handler toggles that class on the
relevant <li> (via a new toggleEmptyView() helper) after a spam/trash
count changes, instead of replacing the view tabs' HTML wholesale.
The currently active view is still never hidden, even at zero count.
- Reverted the AJAX supplemental data / full-HTML-replace added to
_wp_ajax_delete_comment_response() and edit-comments.js in the
previous commit.
Updated the get_views()/views() tests in wpCommentsListTable.php and
wpPostCommentsListTable.php for the new always-present-keys /
CSS-class-based behavior, and removed the AJAX response test that
asserted the reverted approach.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
views() attached each view's " | " separator as the last thing inside the *preceding* view's own <li>, mirroring the parent class. That relied on every view actually being visible, which no longer holds now that empty views stay in the DOM (hidden via is-empty-view) instead of being omitted. A visible view followed only by hidden ones (e.g. "Approved" when "Spam" and "Trash" are both empty) still rendered its own trailing "|", with nothing visible after it to pair with. Attach the separator as a leading marker on each view instead, skipped for hidden views and for the first view that actually renders visible, so a run of hidden views never leaves an orphaned separator next to them regardless of which views are empty. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…to HTML The previous fix attached the " | " separator as a leading marker on each view, computed from which views were empty at render time. That only holds at the moment of rendering: when edit-comments.js later reveals a previously-hidden view by removing its is-empty-view class (e.g. "Trash" becoming non-empty after a comment is trashed), that view's HTML never had a separator baked into it to begin with, so it renders flush against the preceding view with no separator between them. Stop writing the separator as literal text entirely and generate it with CSS instead (`::before` on every non-first <li>, scoped to the Comments screen so other list tables - which still embed the separator as literal text and don't use is-empty-view - aren't affected). A hidden view's own separator is part of its own box, so it hides and reappears together with the view itself automatically, regardless of which direction (PHP's initial render or JS's later toggle) changes its visibility. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…erriding WP_Comments_List_Table::views() duplicated nearly all of WP_List_Table::views() (fetching views, filtering, the empty check, the screen-reader heading, the <ul> wrapper) just to customize how individual <li> elements are printed (adding an is-empty-view class). Split that per-item printing out of WP_List_Table::views() into a new protected print_views() method, so a subclass that needs to customize it can override just that, instead of the whole method. Behavior for every other list table is unchanged - this only moves existing code into its own method. WP_Comments_List_Table now only overrides print_views(). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Trac ticket: https://core.trac.wordpress.org/ticket/66260
Summary
WP_Posts_List_Table::get_views()hides any status filter link (other than "All") whose post count is zero.WP_Comments_List_Table::get_views()had no equivalent, so "Pending", "Approved", "Spam", "Trash", and "Mine" were always rendered even at(0).Per review feedback, applying the Posts list table's logic as-is doesn't fit the Comments screen, which is primarily a moderation queue rather than a plain content list:
?comment_status=moderatedfrom a stale notification, or?comment_status=spamright after "Empty Spam") leaves the page with no visible "current" tab at all — no indication of what's being filtered.Changes
$comment_statusis never hidden, even at zero count.test_get_views_should_return_views_by_default(added for #42066), which asserted the old all-statuses-always-shown behavior this change intentionally revises.How empty views are hidden: CSS class, not DOM omission
An earlier version of this PR had
get_views()omit empty/non-active status entries from its returned array entirely, and had an AJAX moderation action re-render and replace the whole.subsubsubtab list over the wire so a status could appear/disappear live. Per review feedback, that re-render approach was replaced: re-rendering and swapping.subsubsub's HTML on every moderation action would destroy any DOM nodes, event handlers, or data a plugin may have attached to the existing view links — an architectural regression for plugin extensibility, even though it worked fine in isolation on a clean install.Current approach:
get_views()always returns every status key (never omits one); it separately records which ones are currently empty in a new$empty_viewsproperty.WP_Comments_List_Table::views()override (the baseWP_List_Table::views()has no hook for this) adds anis-empty-viewclass to those views'<li>elements. A new CSS rule (.subsubsub li.is-empty-view { display: none; }) hides them — the markup itself always stays in the DOM.edit-comments.js'sdelAfterhandler toggles that class on the relevant<li>(via a newtoggleEmptyView()helper) after a spam/trash count changes, instead of replacing any markup. The currently active view is still never hidden, even at zero count.Test plan
php -l/node --check/npx eslinton all changed files — no errors.Tests_Admin_wpCommentsListTable+Tests_Admin_wpPostCommentsListTable— 15 tests, 54 assertions, all passing.Tests_Ajax_wpAjaxDeleteComment(--group ajax) — 5 tests, 84 assertions, all passing (back to its original count/shape, since the AJAX re-render approach and its test were reverted).get_views(), and passes with it restored.edit-comments.js/.min.jsandlist-tables.css/.min.css/-rtl.cssviagrunt build:js --dev/grunt build:css --devfor local testing.Fix: dangling "|" separator after a hidden trailing view
Spotted after the above: the
|separator between views was attached as the last character inside the preceding view's own<li>(mirroring the parentWP_List_Table::views()), which assumed every view was actually visible. Now that empty views stay in the DOM (just CSS-hidden), a visible view followed only by hidden ones (e.g. "Approved" when "Spam"/"Trash" are both empty) still rendered its own trailing|, dangling with nothing after it. Fixed by attaching the separator as a leading marker on each view instead, skipped for hidden views and the first view that actually renders visible — so it disappears along with whatever's hidden around it, regardless of which combination of views are empty.Fix: separator didn't appear when a hidden view became visible via JS
The previous fix baked the
|separator into each view as a leading marker, computed from which views were empty at render time. That only held for the initial page load: whenedit-comments.jslater reveals a previously-hidden view by removing itsis-empty-viewclass (e.g. "Trash" becoming non-empty after trashing a comment), that view's HTML never had a separator in it to begin with, so it rendered flush against the preceding view with nothing between them.Fixed properly this time: the separator is no longer written as literal text in PHP at all. It's generated by CSS instead (
::beforeon every non-first<li>, scoped to the Comments screen so other list tables — which still embed it as literal text and don't useis-empty-view— aren't affected). A hidden view's own separator is part of its own box, so it disappears and reappears together with the view itself automatically, regardless of which direction (initial PHP render, or a later JS toggle) its visibility changes in.Refactor: share WP_List_Table::views() scaffolding instead of duplicating it
WP_Comments_List_Table::views()duplicated nearly all ofWP_List_Table::views()(fetching/filtering views, the empty check, the screen-reader heading, the<ul>wrapper) just to customize how individual<li>elements are printed. Extracted that per-item printing into a new protectedWP_List_Table::print_views( $views )method that subclasses can override on its own —WP_Comments_List_Tablenow only overridesprint_views(), nothing else. No behavior change for any other list table; this only moves existing code into its own method.