close
Skip to content

Dashboard: Add load more button to On This Day widget - #12804

Open
alshakero wants to merge 5 commits into
WordPress:trunkfrom
alshakero:alshakero/on-this-day-load-more-posts
Open

Dashboard: Add load more button to On This Day widget#12804
alshakero wants to merge 5 commits into
WordPress:trunkfrom
alshakero:alshakero/on-this-day-load-more-posts

Conversation

@alshakero

@alshakero alshakero commented Aug 2, 2026

Copy link
Copy Markdown

What?

Adds a Show more link to the On This Day dashboard widget when more than ten matching posts exist.

Why?

The widget previously limited its query to ten posts, which could make it appear that no additional posts existed.

Trac ticket: https://core.trac.wordpress.org/ticket/65783#ticket

Screenshots

Before After
image image

Testing Instructions

  1. Import this file (good through August): august-2025-five-posts-per-day.xml
  2. The first 10 posts should render, regardless of the year.
  3. Click "Show more"
  4. The rest should show up.
  5. During loading the posts, the button should be disabled.
  6. After loading the posts, the button should be gone.

@github-actions

github-actions Bot commented Aug 2, 2026

Copy link
Copy Markdown

Test using WordPress Playground

The 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

  • All changes will be lost when closing a tab with a Playground instance.
  • All changes will be lost when refreshing the page.
  • A fresh instance is created each time the link below is clicked.
  • Every time this pull request is updated, a new ZIP file containing all changes is created. If changes are not reflected in the Playground instance,
    it's possible that the most recent build failed, or has not completed. Check the list of workflow runs to be sure.

For more details about these limitations and more, check out the Limitations page in the WordPress Playground documentation.

Test this pull request with WordPress Playground.

@alshakero
alshakero marked this pull request as ready for review August 2, 2026 12:54
@github-actions

github-actions Bot commented Aug 2, 2026

Copy link
Copy Markdown

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 props-bot label.

Core Committers: Use this line as a base for the props when committing in SVN:

Props alshakero, peterwilsoncc.

To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook.

@peterwilsoncc

Copy link
Copy Markdown
Contributor

@alshakero I've taken the liberty of merging in trunk to resolve the conflicts following d1c6be6:

  • src/wp-admin/includes/dashboard-on-this-day.php
  • tests/phpunit/tests/admin/wpDashboardOnThisDay.php

Are you able to double check that I haven't missed anything?

I'm now moving on to testing and review.

@peterwilsoncc peterwilsoncc left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As noted inline, this effectively creates an unbound query.

Instead of loading all the posts at once with the unbound query, how about loading the posts_per_page number of posts each time a user clicks the button?

It probably needs work, but something like this could work as a starting point:

diff --git a/src/js/_enqueues/wp/dashboard.js b/src/js/_enqueues/wp/dashboard.js
index 3b3408aecf..f3ca151173 100644
--- a/src/js/_enqueues/wp/dashboard.js
+++ b/src/js/_enqueues/wp/dashboard.js
@@ -191,12 +191,14 @@ jQuery( function($) {
 	window.quickPressLoad();
 
 	/**
-	 * Loads the remaining posts in the On This Day dashboard widget.
+	 * Loads the additional posts in the On This Day dashboard widget.
 	 *
 	 * @since 7.1.0
 	 */
 	$( document ).on( 'click', '#wp_dashboard_on_this_day .wp-on-this-day-load-more button', function() {
 		var $button = $( this ),
+			next_page_count = $button.data( 'wpOnThisDayPostsPerPage' ),
+			total_posts = $button.data('wpOnThisDayTotal'),
 			$widget = $button.closest( '#wp_dashboard_on_this_day' ),
 			$posts = $widget.find( '#wp-on-this-day-posts' );
 
@@ -212,6 +214,7 @@ jQuery( function($) {
 			offset: $button.attr( 'data-wp-on-this-day-offset' )
 		} ).done( function( response ) {
 			var $additional_years,
+				new_offset,
 				post_count;
 
 			if ( ! response.success ) {
@@ -234,7 +237,23 @@ jQuery( function($) {
 			} );
 
 			post_count = response.data.post_count;
-			$button.closest( '.wp-on-this-day-load-more' ).remove();
+			new_offset = $button.data('wpOnThisDayOffset') + post_count;
+			$button.data('wpOnThisDayOffset', new_offset );
+
+			if ( $button.data('wpOnThisDayTotal') <= new_offset ) {
+				$button.closest( '.wp-on-this-day-load-more' ).remove();
+			} else {
+				$button.prop( 'disabled', false );
+				if ( next_page_count > (total_posts - new_offset) ) {
+					next_page_count = total_posts - new_offset;
+				}
+				$button.text(
+					wp.i18n.sprintf (
+						wp.i18n._n( 'Show %s more post', 'Show %s more posts', next_page_count ),
+						next_page_count
+					)
+				);
+			}
 
 			if ( post_count ) {
 				wp.a11y.speak(
diff --git a/src/wp-admin/includes/ajax-actions.php b/src/wp-admin/includes/ajax-actions.php
index 017d0a5d42..f4b7db3ced 100644
--- a/src/wp-admin/includes/ajax-actions.php
+++ b/src/wp-admin/includes/ajax-actions.php
@@ -426,25 +426,16 @@ function wp_ajax_dashboard_on_this_day_load_more() {
 
 	require_once ABSPATH . 'wp-admin/includes/dashboard-on-this-day.php';
 
-	$offset      = isset( $_POST['offset'] ) ? absint( $_POST['offset'] ) : 10;
-	$count_query = _wp_dashboard_on_this_day_get_posts_query(
+	$offset = isset( $_POST['offset'] ) ? absint( $_POST['offset'] ) : 10;
+	$posts  = array();
+
+	$query = _wp_dashboard_on_this_day_get_posts_query(
 		array(
-			'posts_per_page' => 1,
+			'offset'        => $offset,
+			'no_found_rows' => true,
 		)
 	);
-	$post_count  = max( 0, (int) $count_query->found_posts - $offset );
-	$posts       = array();
-
-	if ( $post_count > 0 ) {
-		$query = _wp_dashboard_on_this_day_get_posts_query(
-			array(
-				'posts_per_page' => $post_count,
-				'offset'         => $offset,
-				'no_found_rows'  => true,
-			)
-		);
-		$posts = $query->posts;
-	}
+	$posts = $query->posts;
 
 	ob_start();
 	_wp_dashboard_on_this_day_render_posts( $posts );
diff --git a/src/wp-admin/includes/dashboard-on-this-day.php b/src/wp-admin/includes/dashboard-on-this-day.php
index a1dc92f59f..c5d797afd2 100644
--- a/src/wp-admin/includes/dashboard-on-this-day.php
+++ b/src/wp-admin/includes/dashboard-on-this-day.php
@@ -108,15 +108,21 @@ function wp_dashboard_on_this_day() {
 					class="button-link"
 					data-wp-on-this-day-nonce="<?php echo esc_attr( wp_create_nonce( 'wp_dashboard_on_this_day_load_more' ) ); ?>"
 					data-wp-on-this-day-offset="<?php echo esc_attr( $displayed_post_count ); ?>"
+					data-wp-on-this-day-total="<?php echo esc_attr( $post_count ); ?>"
+					data-wp-on-this-day-posts-per-page="<?php echo esc_attr( get_option( 'posts_per_page' ) ); ?>"
 					aria-controls="wp-on-this-day-posts"
 				>
 					<?php
+					$next_page_count = get_option( 'posts_per_page' );
+					if ( $next_page_count > ( $post_count - $displayed_post_count ) ) {
+						$next_page_count = $post_count - $displayed_post_count;
+					}
 					printf(
 						esc_html(
 							/* translators: %s: Number of additional posts. */
-							_n( 'Show %s more post', 'Show %s more posts', $post_count - $displayed_post_count )
+							_n( 'Show %s more post', 'Show %s more posts', $next_page_count )
 						),
-						esc_html( number_format_i18n( $post_count - $displayed_post_count ) )
+						esc_html( number_format_i18n( $next_page_count ) )
 					);
 					?>
 				</button>
@@ -248,7 +254,6 @@ function _wp_dashboard_on_this_day_get_posts_query( $query_args = array() ) {
 		array(
 			'post_type'              => 'post',
 			'post_status'            => array( 'publish' ),
-			'posts_per_page'         => 10,
 			'ignore_sticky_posts'    => true,
 			'orderby'                => 'date',
 			'order'                  => 'DESC',
diff --git a/src/wp-includes/script-loader.php b/src/wp-includes/script-loader.php
index fd1f24a089..78b88d13f0 100644
--- a/src/wp-includes/script-loader.php
+++ b/src/wp-includes/script-loader.php
@@ -1506,7 +1506,7 @@ function wp_default_scripts( $scripts ) {
 		$scripts->add( 'wp-color-picker', "/wp-admin/js/color-picker$suffix.js", array( 'iris' ), false, 1 );
 		$scripts->set_translations( 'wp-color-picker' );
 
-		$scripts->add( 'dashboard', "/wp-admin/js/dashboard$suffix.js", array( 'common', 'jquery', 'admin-comments', 'postbox', 'wp-util', 'wp-a11y', 'wp-date' ), false, 1 );
+		$scripts->add( 'dashboard', "/wp-admin/js/dashboard$suffix.js", array( 'common', 'jquery', 'admin-comments', 'postbox', 'wp-util', 'wp-a11y', 'wp-date', 'wp-i18n' ), false, 1 );
 		$scripts->set_translations( 'dashboard' );
 
 		$scripts->add( 'list-revisions', "/wp-includes/js/wp-list-revisions$suffix.js" );

Comment thread src/wp-admin/includes/ajax-actions.php Outdated
Comment on lines +430 to +435
$count_query = _wp_dashboard_on_this_day_get_posts_query(
array(
'posts_per_page' => 1,
)
);
$post_count = max( 0, (int) $count_query->found_posts - $offset );

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This effectively generates an unbound query. Or, to be very precise, an unbound - 10 query.

To use the example I used on the original PR: I used to work at a news paper that produces hundreds of articles a day and they've been using WordPress since 2015. Even if they've only created 100 posts a day, this will end up querying 10,000 posts.

Even on the most high-end servers imaginable, that will cause problems.

Comment thread src/js/_enqueues/wp/dashboard.js Outdated

if ( post_count ) {
wp.a11y.speak(
wp.i18n.sprintf(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wp-i18n needs to added as a dependency for the dashboard script in script-loader.php.

@alshakero

Copy link
Copy Markdown
Author

@peterwilsoncc I pushed pagination support, but I'm conflicted about my approach to the CSS. I reused CSS from WP_List_Table. On one hand, the CSS is there and it's a shame to duplicate it, on the other hand, it ties two unrelated things together. WDYT? This doesn't use any JS or AJAX, it's quite clean otherwise.

@peterwilsoncc peterwilsoncc left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks Omar,

I agree with your approach to duplicate the CSS. It's a very different part of the admin so unlikely devs would think to check this widget while working on the list table.

I think the pagination looks nicer in the current iteration but it would be good to use an AJAX request to navigate rather than needing to reload the entire dashboard. It will be a nicer user experience and reduce the number of database queries when paginating the widget.

Using a get parameter for the current page is a nice touch, it will allow devs to filter the number of posts via the wp_dashboard_on_this_day_query_args filter.

@peterwilsoncc

Copy link
Copy Markdown
Contributor

@alshakero As I was typing out the above, a ticket was opened to punt the widget to WordPress 7.2 (See Core-65801), so there's no rush on anything I've mentioned.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants