Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
Prev Previous commit
Next Next commit
Add admin pointers for new features
  • Loading branch information
westonruter committed Aug 9, 2025
commit 3296195a546e56b478d50bbe4a9510d1b8d94aa2
142 changes: 108 additions & 34 deletions plugins/performance-lab/includes/admin/load.php
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,36 @@ function perflab_render_settings_page(): void {
<?php
}

/**
* Gets dismissed admin pointer IDs.
*
* @since n.e.x.t
*
* @return non-empty-string[] Dismissed admin pointer IDs.
*/
function perflab_get_dismissed_admin_pointer_ids(): array {
return array_filter(
explode(
',',
(string) get_user_meta( get_current_user_id(), 'dismissed_wp_pointers', true )
)
);
}

/**
* Gets the admin pointers.
*
* @since n.e.x.t
*
* @return array<non-empty-string, string> Admin pointer messages with the admin pointer IDs as the keys.
*/
function perflab_get_admin_pointers(): array {
return array(
'perflab-admin-pointer' => __( 'You can now test upcoming WordPress performance features.', 'performance-lab' ),
'perflab-feature-view-transitions' => __( 'New <strong>View Transitions</strong> feature now available.', 'performance-lab' ),
);
}

/**
* Initializes admin pointer.
*
Expand All @@ -84,18 +114,27 @@ function perflab_admin_pointer( ?string $hook_suffix = '' ): void {
return;
}
$current_user = get_current_user_id();
$dismissed = array_filter( explode( ',', (string) get_user_meta( get_current_user_id(), 'dismissed_wp_pointers', true ) ) );
$pointers = array_keys( perflab_get_admin_pointers() );
$dismissed = perflab_get_dismissed_admin_pointer_ids();

if ( in_array( 'perflab-admin-pointer', $dismissed, true ) ) {
// All pointers have been dismissed already.
if ( count( array_diff( $pointers, $dismissed ) ) === 0 ) {
return;
}

// Do not show the admin pointer when not on the dashboard or plugins list table.
if ( ! in_array( $hook_suffix, array( 'index.php', 'plugins.php' ), true ) ) {

// Do not show on the settings page and dismiss the pointer.
if ( isset( $_GET['page'] ) && PERFLAB_SCREEN === $_GET['page'] && ( ! in_array( 'perflab-admin-pointer', $dismissed, true ) ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
$dismissed[] = 'perflab-admin-pointer';
update_user_meta( $current_user, 'dismissed_wp_pointers', implode( ',', $dismissed ) );
// And if we're on the Performance screen, automatically dismiss the pointers.
if ( isset( $_GET['page'] ) && PERFLAB_SCREEN === $_GET['page'] ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
update_user_meta(
$current_user,
'dismissed_wp_pointers',
implode(
',',
array_unique( array_merge( $dismissed, $pointers ) )
)
);
}

return;
Expand All @@ -113,53 +152,84 @@ function perflab_admin_pointer( ?string $hook_suffix = '' ): void {
*
* Handles the rendering of the admin pointer.
*
* @todo Eliminate this function in favor of putting it inside of perflab_admin_pointer() which attaches an inline script.
*
* @since 1.0.0
* @since 2.4.0 Optional arguments were added to make the function reusable for different pointers.
*
* @param string $pointer_id Optional. ID of the pointer. Default 'perflab-admin-pointer'.
* @param array{heading?: string, content?: string} $args Optional. Pointer arguments. Supports 'heading' and 'content' entries.
* Defaults are the heading and content for the 'perflab-admin-pointer'.
* @since n.e.x.t Unused arguments removed.
*/
function perflab_render_pointer( string $pointer_id = 'perflab-admin-pointer', array $args = array() ): void {
if ( ! isset( $args['heading'] ) ) {
$args['heading'] = __( 'Performance Lab', 'performance-lab' );
}
if ( ! isset( $args['content'] ) ) {
$args['content'] = sprintf(
/* translators: %s: settings page link */
esc_html__( 'You can now test upcoming WordPress performance features. Open %s to individually toggle the performance features.', 'performance-lab' ),
'<a href="' . esc_url( add_query_arg( 'page', PERFLAB_SCREEN, admin_url( 'options-general.php' ) ) ) . '">' . esc_html__( 'Settings > Performance', 'performance-lab' ) . '</a>'
);
function perflab_render_pointer(): void {
$new_install_pointer_id = 'perflab-admin-pointer';
$perflab_admin_pointers = perflab_get_admin_pointers();
$dismissed_pointer_ids = perflab_get_dismissed_admin_pointer_ids();

if ( ! in_array( $new_install_pointer_id, $dismissed_pointer_ids, true ) ) {
$needed_pointer_ids = array( $new_install_pointer_id );
} else {
$needed_pointer_ids = array_diff( array_keys( $perflab_admin_pointers ), $dismissed_pointer_ids );
}

$args = array(
'heading' => __( 'Performance Lab', 'performance-lab' ),
);

$args['content'] = implode(
'',
array_map(
static function ( string $needed_pointer ) use ( $perflab_admin_pointers ): string {
return '<p>' . $perflab_admin_pointers[ $needed_pointer ] . '</p>';
},
$needed_pointer_ids
)
);

$args['content'] .= '<p>' . sprintf(
/* translators: %s: settings page link */
esc_html__( 'Open %s to individually toggle the performance features.', 'performance-lab' ),
'<a href="' . esc_url( add_query_arg( 'page', PERFLAB_SCREEN, admin_url( 'options-general.php' ) ) ) . '">' . esc_html__( 'Settings > Performance', 'performance-lab' ) . '</a>'
) . '</p>';

$wp_kses_options = array(
'a' => array(
'a' => array(
'href' => array(),
),
'p' => array(),
'strong' => array(),
);

$pointer_ids_to_dismiss = array_values( array_diff( array_keys( $perflab_admin_pointers ), $dismissed_pointer_ids ) );
?>
<script id="<?php echo esc_attr( $pointer_id ); ?>" type="text/javascript">
<script>
jQuery( function() {
const pointerIdsToDismiss = <?php echo wp_json_encode( $pointer_ids_to_dismiss, JSON_OBJECT_AS_ARRAY ); ?>;
const nonce = <?php echo wp_json_encode( wp_create_nonce( 'dismiss_pointer' ) ); ?>;

function dismissNextPointer() {
const pointerId = pointerIdsToDismiss.shift();
if ( ! pointerId ) {
return;
}

jQuery.post(
window.ajaxurl,
{
pointer: pointerId,
action: 'dismiss-wp-pointer',
_wpnonce: nonce,
}
).then( dismissNextPointer );
}

// Pointer Options.
const options = {
content: <?php echo wp_json_encode( '<h3>' . esc_html( $args['heading'] ) . '</h3><p>' . wp_kses( $args['content'], $wp_kses_options ) . '</p>' ); ?>,
content: <?php echo wp_json_encode( '<h3>' . esc_html( $args['heading'] ) . '</h3>' . wp_kses( $args['content'], $wp_kses_options ) ); ?>,
position: {
edge: 'left',
align: 'right',
},
pointerClass: 'wp-pointer arrow-top',
pointerWidth: 420,
close: function() {
jQuery.post(
window.ajaxurl,
{
pointer: <?php echo wp_json_encode( $pointer_id ); ?>,
action: 'dismiss-wp-pointer',
_wpnonce: <?php echo wp_json_encode( wp_create_nonce( 'dismiss_pointer' ) ); ?>,
}
);
}
close: dismissNextPointer
};

jQuery( '#menu-settings' ).pointer( options ).pointer( 'open' );
Expand Down Expand Up @@ -207,7 +277,11 @@ function perflab_plugin_action_links_add_settings( $links ) {
* @since 2.3.0
*/
function perflab_dismiss_wp_pointer_wrapper(): void {
if ( isset( $_POST['pointer'] ) && 'perflab-admin-pointer' !== $_POST['pointer'] ) {
if (
isset( $_POST['pointer'] )
&&
! in_array( $_POST['pointer'], array_keys( perflab_get_admin_pointers() ), true )
) {
// Another plugin's pointer, do nothing.
return;
}
Expand Down
48 changes: 44 additions & 4 deletions plugins/performance-lab/tests/includes/admin/test-load.php
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,37 @@ public function test_perflab_render_settings_page(): void {
$this->assertStringNotContainsString( "<input type='hidden' name='option_page' value='" . PERFLAB_SCREEN . "' />", $output );
}

/**
* @covers ::perflab_get_dismissed_admin_pointer_ids
*/
public function test_perflab_get_dismissed_admin_pointer_ids(): void {
$user_id = self::factory()->user->create( array( 'role' => 'administrator' ) );
wp_set_current_user( $user_id );

// No dismissed pointers.
$this->assertSame( array(), perflab_get_dismissed_admin_pointer_ids() );

// Dismiss a single pointer.
update_user_meta( $user_id, 'dismissed_wp_pointers', 'perflab-admin-pointer' );
$this->assertSame( array( 'perflab-admin-pointer' ), perflab_get_dismissed_admin_pointer_ids() );

// Dismiss multiple pointers.
update_user_meta( $user_id, 'dismissed_wp_pointers', 'perflab-admin-pointer,another-pointer' );
$this->assertSame( array( 'perflab-admin-pointer', 'another-pointer' ), perflab_get_dismissed_admin_pointer_ids() );

// Dismiss all pointers.
update_user_meta( $user_id, 'dismissed_wp_pointers', implode( ',', array_keys( perflab_get_admin_pointers() ) ) );
$this->assertSame( array_keys( perflab_get_admin_pointers() ), perflab_get_dismissed_admin_pointer_ids() );
}

/**
* @covers ::perflab_get_admin_pointers
*/
public function test_perflab_get_admin_pointers(): void {
$pointers = perflab_get_admin_pointers();
$this->assertArrayHasKey( 'perflab-admin-pointer', $pointers );
}
Comment on lines +89 to +118
Copy link
Member Author

Choose a reason for hiding this comment

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

Note: I'm happy to say that Gemini Code Assist was actually able to write most of this for me. In PhpStorm, I just did Cmd+\ and then entered /generate test perflab_get_dismissed_admin_pointer_ids and then again for perflab_get_admin_pointers(), and voilà!


/**
* @return array<string, array{ hook_suffix: string|null, expected: bool }>
*/
Expand Down Expand Up @@ -119,23 +150,32 @@ public function data_provider_test_perflab_admin_pointer(): array {
'assert' => null,
'dismissed_wp_pointers' => '',
),
'dashboard_yes_dismissed' => array(
'dashboard_new_dismissed' => array(
'set_up' => static function (): void {
update_user_meta( wp_get_current_user()->ID, 'dismissed_wp_pointers', 'perflab-admin-pointer' );
},
'hook_suffix' => 'index.php',
'expected' => false,
'expected' => true,
'assert' => null,
'dismissed_wp_pointers' => 'perflab-admin-pointer',
),
'dashboard_all_dismissed' => array(
'set_up' => static function (): void {
update_user_meta( wp_get_current_user()->ID, 'dismissed_wp_pointers', implode( ',', array_keys( perflab_get_admin_pointers() ) ) );
},
'hook_suffix' => 'index.php',
'expected' => false,
'assert' => null,
'dismissed_wp_pointers' => implode( ',', array_keys( perflab_get_admin_pointers() ) ),
),
'perflab_screen_first_time' => array(
'set_up' => static function (): void {
$_GET['page'] = PERFLAB_SCREEN;
},
'hook_suffix' => 'options-general.php',
'expected' => false,
'assert' => null,
'dismissed_wp_pointers' => 'perflab-admin-pointer',
'dismissed_wp_pointers' => implode( ',', array_keys( perflab_get_admin_pointers() ) ),
),
'perflab_screen_second_time' => array(
'set_up' => static function (): void {
Expand All @@ -145,7 +185,7 @@ public function data_provider_test_perflab_admin_pointer(): array {
'hook_suffix' => 'options-general.php',
'expected' => false,
'assert' => null,
'dismissed_wp_pointers' => 'perflab-admin-pointer',
'dismissed_wp_pointers' => implode( ',', array_keys( perflab_get_admin_pointers() ) ),
),
);
}
Expand Down