Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 19 additions & 1 deletion src/wp-admin/includes/dashboard.php
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,7 @@ function wp_dashboard_setup() {
*
* @since 2.7.0
* @since 5.6.0 The `$context` and `$priority` parameters were added.
* @since 7.2.0 The `$enqueue_callback` parameter was added.
*
* @global callable[] $wp_dashboard_control_callbacks
*
Expand All @@ -184,12 +185,29 @@ function wp_dashboard_setup() {
* Accepts 'normal', 'side', 'column3', or 'column4'. Default 'normal'.
* @param string $priority Optional. The priority within the context where the box should show.
* Accepts 'high', 'core', 'default', or 'low'. Default 'core'.
* @param callable $enqueue_callback Optional. Function that enqueues the scripts and styles for the widget.
* Only called when the widget is not hidden via Screen Options, so assets
* for hidden widgets are not loaded. Default null.
*/
function wp_add_dashboard_widget( $widget_id, $widget_name, $callback, $control_callback = null, $callback_args = null, $context = 'normal', $priority = 'core' ) {
function wp_add_dashboard_widget( $widget_id, $widget_name, $callback, $control_callback = null, $callback_args = null, $context = 'normal', $priority = 'core', $enqueue_callback = null ) {
global $wp_dashboard_control_callbacks;

$screen = get_current_screen();

/*
* Only enqueue the widget's assets when the widget is actually visible.
* A widget hidden via Screen Options is removed from the DOM, so loading
* its scripts and styles wastes resources. Collapsed (closed) widgets are
* still present in the DOM, so they are intentionally treated as visible.
*/
if ( is_callable( $enqueue_callback ) && $screen instanceof WP_Screen ) {
$hidden_widgets = get_hidden_meta_boxes( $screen );

if ( ! in_array( $widget_id, $hidden_widgets, true ) ) {
call_user_func( $enqueue_callback );
}
}

$private_callback_args = array( '__widget_basename' => $widget_name );

if ( is_null( $callback_args ) ) {
Expand Down
172 changes: 172 additions & 0 deletions tests/phpunit/tests/admin/includesTemplate.php
Original file line number Diff line number Diff line change
Expand Up @@ -495,6 +495,178 @@ public function test_wp_add_dashboard_widget() {
remove_meta_box( 'dashboard2', 'dashboard', 'normal' );
}

/**
* Tests that the enqueue callback runs when the widget is visible.
*
* @ticket 55344
*
* @covers ::wp_add_dashboard_widget
*/
public function test_wp_add_dashboard_widget_enqueue_callback_called_when_visible() {
set_current_screen( 'dashboard' );

if ( ! function_exists( 'wp_add_dashboard_widget' ) ) {
require_once ABSPATH . 'wp-admin/includes/dashboard.php';
}

$called = false;

wp_add_dashboard_widget(
'dashboard_enqueue_visible',
'Visible',
'__return_false',
null,
null,
'normal',
'core',
function () use ( &$called ) {
$called = true;
}
);

$this->assertTrue( $called, 'The enqueue callback should run for a visible widget.' );

remove_meta_box( 'dashboard_enqueue_visible', 'dashboard', 'normal' );
}

/**
* Tests that the enqueue callback does not run when the widget is hidden
* via Screen Options.
*
* @ticket 55344
*
* @covers ::wp_add_dashboard_widget
*/
public function test_wp_add_dashboard_widget_enqueue_callback_not_called_when_hidden() {
set_current_screen( 'dashboard' );

if ( ! function_exists( 'wp_add_dashboard_widget' ) ) {
require_once ABSPATH . 'wp-admin/includes/dashboard.php';
}

wp_set_current_user( self::$editor_id );
update_user_option( get_current_user_id(), 'metaboxhidden_dashboard', array( 'dashboard_enqueue_hidden' ), true );

$called = false;

wp_add_dashboard_widget(
'dashboard_enqueue_hidden',
'Hidden',
'__return_false',
null,
null,
'normal',
'core',
function () use ( &$called ) {
$called = true;
}
);

$this->assertFalse( $called, 'The enqueue callback should not run for a hidden widget.' );

remove_meta_box( 'dashboard_enqueue_hidden', 'dashboard', 'normal' );
}

/**
* Tests that the enqueue callback runs for a collapsed (closed) widget,
* because a collapsed widget is still present in the DOM.
*
* @ticket 55344
*
* @covers ::wp_add_dashboard_widget
*/
public function test_wp_add_dashboard_widget_enqueue_callback_called_when_closed_but_not_hidden() {
set_current_screen( 'dashboard' );

if ( ! function_exists( 'wp_add_dashboard_widget' ) ) {
require_once ABSPATH . 'wp-admin/includes/dashboard.php';
}

wp_set_current_user( self::$editor_id );
update_user_option( get_current_user_id(), 'closedpostboxes_dashboard', array( 'dashboard_enqueue_closed' ), true );

$called = false;

wp_add_dashboard_widget(
'dashboard_enqueue_closed',
'Closed',
'__return_false',
null,
null,
'normal',
'core',
function () use ( &$called ) {
$called = true;
}
);

$this->assertTrue( $called, 'The enqueue callback should run for a collapsed (closed) widget.' );

remove_meta_box( 'dashboard_enqueue_closed', 'dashboard', 'normal' );
}

/**
* Tests that a null enqueue callback is accepted without error,
* preserving backward compatibility for existing callers.
*
* @ticket 55344
*
* @covers ::wp_add_dashboard_widget
*/
public function test_wp_add_dashboard_widget_null_enqueue_callback_no_error() {
global $wp_meta_boxes;

set_current_screen( 'dashboard' );

if ( ! function_exists( 'wp_add_dashboard_widget' ) ) {
require_once ABSPATH . 'wp-admin/includes/dashboard.php';
}

wp_add_dashboard_widget( 'dashboard_enqueue_null', 'Null', '__return_false', null, null, 'normal', 'core', null );

$this->assertArrayHasKey( 'dashboard_enqueue_null', $wp_meta_boxes['dashboard']['normal']['core'] );

remove_meta_box( 'dashboard_enqueue_null', 'dashboard', 'normal' );
}

/**
* Tests that the enqueue callback runs on a first visit, when no user
* preference for hidden meta boxes has been stored yet.
*
* @ticket 55344
*
* @covers ::wp_add_dashboard_widget
*/
public function test_wp_add_dashboard_widget_enqueue_callback_called_when_no_user_pref() {
set_current_screen( 'dashboard' );

if ( ! function_exists( 'wp_add_dashboard_widget' ) ) {
require_once ABSPATH . 'wp-admin/includes/dashboard.php';
}

wp_set_current_user( self::$editor_id );
delete_user_option( get_current_user_id(), 'metaboxhidden_dashboard', true );

$called = false;

wp_add_dashboard_widget(
'dashboard_enqueue_no_pref',
'No Pref',
'__return_false',
null,
null,
'normal',
'core',
function () use ( &$called ) {
$called = true;
}
);

$this->assertTrue( $called, 'The enqueue callback should run when no hidden-widget preference is stored.' );

remove_meta_box( 'dashboard_enqueue_no_pref', 'dashboard', 'normal' );
}

/**
* Tests that get_post_states() handles a null value gracefully.
*
Expand Down
Loading