Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
0af1815
Add quick poc.
zutigrm Oct 1, 2025
487f5a4
Remove get_callback.
zutigrm Oct 1, 2025
1b761b3
Add wp core CTA class.
zutigrm Oct 1, 2025
fd0f924
Switch to button.
zutigrm Oct 1, 2025
5f023e3
Add classes and styles.
zutigrm Oct 1, 2025
8e4c3d3
Merge remote-tracking branch 'origin/develop' into enhancement/11433-…
benbowler Oct 21, 2025
c0a6af6
Implement Pointer for Email Reporting feature.
benbowler Oct 22, 2025
5eba5ab
Bump to trigger jobs.
benbowler Oct 22, 2025
ff78207
Bump to trigger jobs.
benbowler Oct 22, 2025
47ebace
Address code review comments.
benbowler Oct 27, 2025
fce94da
Remove commneted out code.
zutigrm Oct 27, 2025
d0adeb9
Remove commented out code.
benbowler Oct 27, 2025
48c154f
Merge branch 'enhancement/11433-pue-pointer' of github.com:google/sit…
benbowler Oct 27, 2025
bc41306
Merge remote-tracking branch 'origin/develop' into enhancement/11433-…
benbowler Oct 28, 2025
94cd93a
Refactor button implementation to remove JS passed as prop. Code revi…
benbowler Oct 29, 2025
47e7457
Fix test failure with removed class.
benbowler Oct 29, 2025
9345c13
Merge branch 'develop' into enhancement/11433-pue-pointer.
aaemnnosttv Oct 29, 2025
b34109c
Refactor pointer script construction.
aaemnnosttv Oct 30, 2025
841032a
Update email reporting pointer tests.
aaemnnosttv Oct 30, 2025
90e884c
Fix email reporting tests and additional simplifications.
aaemnnosttv Oct 30, 2025
358156f
Update includes/Core/Email_Reporting/Email_Reporting.php
benbowler Nov 3, 2025
e7b5dea
Refactor Core Dashboard Effects to hooks and clean pointer implmentat…
benbowler Nov 3, 2025
a2b9b12
Merge remote-tracking branch 'origin/develop' into enhancement/11433-…
benbowler Nov 3, 2025
ba7e407
Merge branch 'enhancement/11433-pue-pointer' of github.com:google/sit…
benbowler Nov 3, 2025
a3feaed
Fix dismissal bugs.
benbowler Nov 3, 2025
520c432
Merge branch 'develop' into enhancement/11433-pue-pointer.
aaemnnosttv Nov 5, 2025
3938ab7
Remove JS from pointer test.
aaemnnosttv Nov 5, 2025
6da1d4f
Update dependency group.
aaemnnosttv Nov 5, 2025
53eac8d
Replace escaped quotes.
aaemnnosttv Nov 5, 2025
a6a3b26
Restore context to use absolute URL in View Only pointer.
aaemnnosttv Nov 5, 2025
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
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/**
* ScrollEffect component.
* CoreDashboardEffects component.
*
* Site Kit by Google, Copyright 2022 Google LLC
* Site Kit by Google, Copyright 2025 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -19,9 +19,11 @@
/**
* Internal dependencies
*/
import useOpenEmailReportingSelectionPanelEffect from '@/js/hooks/useOpenEmailReportingSelectionPanelEffect';
import { useHasScrolledEffect } from '@/js/hooks/useHasScrolledEffect';

export default function ScrollEffect() {
export default function CoreDashboardEffects() {
Copy link
Collaborator

Choose a reason for hiding this comment

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

One important difference here with the Module version is that modules can register two different effect components – one for the main and entity dashboards respectively. Core doesn't need to register its effects so its components are simpler, but it would make sense to follow the same pattern where this top level component renders the dashboard-specific variant based on the context.

If this is getting out of scope we could address it in a follow up too, I just wanted to highlight this detail for maintaining consistency.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I've continued with a single component for now across main and entity dashboards and created #11712 to split this out in future.

useOpenEmailReportingSelectionPanelEffect();
useHasScrolledEffect();

return null;
Expand Down
6 changes: 3 additions & 3 deletions assets/js/components/DashboardEntityApp.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,11 +57,11 @@ import { Cell, Grid, Row } from '@/js/material-components';
import PageHeader from './PageHeader';
import Layout from './layout/Layout';
import { CORE_WIDGETS } from '@/js/googlesitekit/widgets/datastore/constants';
import ScrollEffect from './ScrollEffect';
import DashboardSharingSettingsButton from './dashboard-sharing/DashboardSharingSettingsButton';
import useViewOnly from '@/js/hooks/useViewOnly';
import OfflineNotification from './notifications/OfflineNotification';
import { useMonitorInternetConnection } from '@/js/hooks/useMonitorInternetConnection';
import CoreDashboardEffects from './CoreDashboardEffects';
import ModuleDashboardEffects from './ModuleDashboardEffects';
import UserSettingsSelectionPanel from './email-reporting/UserSettingsSelectionPanel';
import { useFeature } from '@/js/hooks/useFeature';
Expand Down Expand Up @@ -144,7 +144,7 @@ function DashboardEntityApp() {
if ( currentEntityURL === null ) {
return (
<div className="googlesitekit-widget-context googlesitekit-module-page googlesitekit-entity-dashboard">
<ScrollEffect />
<CoreDashboardEffects />
<ModuleDashboardEffects />
<Grid>
<Row>
Expand Down Expand Up @@ -217,7 +217,7 @@ function DashboardEntityApp() {
}
return (
<Fragment>
<ScrollEffect />
<CoreDashboardEffects />
<ModuleDashboardEffects />
<Header showNavigation>
<EntitySearchInput />
Expand Down
4 changes: 2 additions & 2 deletions assets/js/components/DashboardMainApp.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,6 @@ import DateRangeSelector from './DateRangeSelector';
import HelpMenu from './help/HelpMenu';
import SurveyViewTrigger from './surveys/SurveyViewTrigger';
import CurrentSurveyPortal from './surveys/CurrentSurveyPortal';
import ScrollEffect from './ScrollEffect';
import MetricsSelectionPanel from './KeyMetrics/MetricsSelectionPanel';
import UserSettingsSelectionPanel from './email-reporting/UserSettingsSelectionPanel';
import { useFeature } from '@/js/hooks/useFeature';
Expand All @@ -68,6 +67,7 @@ import useViewOnly from '@/js/hooks/useViewOnly';
import { CORE_FORMS } from '@/js/googlesitekit/datastore/forms/constants';
import OfflineNotification from './notifications/OfflineNotification';
import ModuleDashboardEffects from './ModuleDashboardEffects';
import CoreDashboardEffects from './CoreDashboardEffects';
import { useBreakpoint } from '@/js/hooks/useBreakpoint';
import { useMonitorInternetConnection } from '@/js/hooks/useMonitorInternetConnection';
import useQueryArg from '@/js/hooks/useQueryArg';
Expand Down Expand Up @@ -248,7 +248,7 @@ export default function DashboardMainApp() {

return (
<Fragment>
<ScrollEffect />
<CoreDashboardEffects />
<ModuleDashboardEffects />

<AdminScreenTooltip />
Expand Down
45 changes: 45 additions & 0 deletions assets/js/hooks/useOpenEmailReportingSelectionPanelEffect.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/**
* Open Email Reporting Selection Panel hook.
*
* Site Kit by Google, Copyright 2025 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

/**
* External dependencies
*/
import { useMount } from 'react-use';
Copy link
Collaborator

Choose a reason for hiding this comment

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

This is an external dependency, not WP.


/**
* Internal dependencies
*/
import { useDispatch } from 'googlesitekit-data';
import useQueryArg from '@/js/hooks/useQueryArg';
import { CORE_UI } from '@/js/googlesitekit/datastore/ui/constants';
import { USER_SETTINGS_SELECTION_PANEL_OPENED_KEY } from '@/js/components/email-reporting/constants';

export default function useOpenEmailReportingSelectionPanelEffect() {
const [ emailReportingPanelOpen, setEmailReportingPanelOpen ] = useQueryArg(
'email-reporting-panel'
);
const { setValue: setUIValue } = useDispatch( CORE_UI );

useMount( () => {
// If redirected from a pointer CTA or following link from email footer, open the Email Reporting selection panel.
if ( emailReportingPanelOpen !== undefined ) {
setUIValue( USER_SETTINGS_SELECTION_PANEL_OPENED_KEY, true );
setEmailReportingPanelOpen( undefined );
}
} );
}
58 changes: 58 additions & 0 deletions assets/sass/components/wp-dashboard/googlesitekit-pointers.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/**
* Pointers styles for WP Dashboard
*
* Site Kit by Google, Copyright 2025 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

.googlesitekit-pointer-cta--dismiss {
Copy link
Collaborator

Choose a reason for hiding this comment

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

This doesn't quite look like the design. This is what I see

Image

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

In the designs the X icon takes the color from the Pointer header, however, to maintain compatibility with the WP theme the pointer header will take the primary color of the theme. Therefore I went with black for this icon to work across all possible customised theme colors.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Let's iterate on this in a follow up as we should be able to match it even with custom admin themes.

align-items: center;
background-color: $c-white;
border: none;
border-radius: 50%;
display: flex;
justify-content: center;
position: absolute;
right: 10px;
top: 12px;

&:hover {
cursor: pointer;
}
}

.googlesitekit-pointer-buttons {
margin-top: 55px;
padding-bottom: 0;
padding-left: 20px;
padding-right: 20px;
}

.googlesitekit-email-pointer {
.wp-pointer-content h3::before {
content: "\f466"; // Email icon
font-family: dashicons, sans-serif;
margin-right: 8px;
}

.wp-pointer-content h4 {
line-height: 24px;
margin-bottom: 0;
margin-top: 16px;
}

.wp-pointer-content p {
margin-top: 0;
}
}
1 change: 1 addition & 0 deletions assets/sass/wpdashboard.scss
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@
@import "components/wp-dashboard/googlesitekit-unique-visitors-chart-widget";
@import "components/wp-dashboard/googlesitekit-wp-dashboard";
@import "components/wp-dashboard/googlesitekit-wp-dashboard-stats";
@import "components/wp-dashboard/googlesitekit-pointers";

// Charts
@import "components/dashboard/googlesitekit-dashboard-charts";
Expand Down
24 changes: 24 additions & 0 deletions includes/Core/Admin/Pointer.php
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,8 @@ public function __construct( $slug, array $args ) {
'target_id' => '',
'position' => 'top',
'active_callback' => null,
'buttons' => null,
'class' => '',
)
);
}
Expand All @@ -90,6 +92,28 @@ public function get_title() {
return $this->args['title'];
}

/**
* Gets the pointer buttons.
*
* @since n.e.x.t
*
* @return string Pointer buttons.
*/
public function get_buttons() {
return $this->args['buttons'];
}

/**
* Gets the pointer custom class.
*
* @since n.e.x.t
*
* @return string|array Pointer custom class.
*/
public function get_class() {
return $this->args['class'];
}

/**
* Gets the pointer content.
*
Expand Down
105 changes: 76 additions & 29 deletions includes/Core/Admin/Pointers.php
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@ function ( Pointer $pointer ) use ( $hook_suffix ) {
}

wp_enqueue_style( 'wp-pointer' );
// Dashboard styles are required where pointers are used to ensure proper styling.
wp_enqueue_style( 'googlesitekit-wp-dashboard-css' );
wp_enqueue_script( 'wp-pointer' );

add_action(
Expand Down Expand Up @@ -103,6 +105,7 @@ function ( $pointer ) {
* Prints script for a given pointer.
*
* @since 1.83.0
* @since n.e.x.t Updated to support buttons and header dismiss icon.
*
* @param Pointer $pointer Pointer to print.
*/
Expand All @@ -112,38 +115,82 @@ private function print_pointer_script( $pointer ) {
return;
}

$slug = $pointer->get_slug();
$buttons = $pointer->get_buttons();
if ( $buttons ) {
// Content including buttons escaped below in the inline script with wp_kses.
$content .= '<div class="googlesitekit-pointer-buttons">' . $buttons . '</div>';
}

BC_Functions::wp_print_inline_script_tag(
sprintf(
'
jQuery( function() {
var options = {
content: "<h3>%s</h3>%s",
position: %s,
pointerWidth: 420,
close: function() {
jQuery.post(
window.ajaxurl,
{
pointer: "%s",
action: "dismiss-wp-pointer",
}
);
}
};

jQuery( "#%s" ).pointer( options ).pointer( "open" );
} );
',
esc_js( $pointer->get_title() ),
$content,
wp_json_encode( $pointer->get_position() ),
esc_js( $slug ),
esc_js( $pointer->get_target_id() )
$class = array( 'wp-pointer' );
if ( $pointer->get_class() ) {
$class[] = $pointer->get_class();
}

$kses_title = array(
'span' => array( 'class' => array() ),
'button' => array(
'class' => array(),
'type' => array(),
'data-action' => array(),
),
);

$kses_content = array(
'a' => array(
'href' => array(),
'class' => array(),
'target' => array(),
'rel' => array(),
'data-action' => array(),
),
'h4' => array(),
'p' => array( 'class' => array() ),
'br' => array(),
'strong' => array(),
'em' => array(),
'button' => array(
'class' => array(),
'type' => array(),
'data-action' => array(),
),
'div' => array( 'class' => array() ),
);

BC_Functions::wp_print_inline_script_tag(
<<<'JS'
(
function ( $, wp, config ) {
function initPointer() {
const options = {
content: '<h3>' + config.title + '</h3>' + config.content,
position: JSON.parse( config.position ),
pointerWidth: 420,
pointerClass: config.class,
close: function() {
wp.ajax.post( 'dismiss-wp-pointer', { pointer: config.slug } );
},
buttons: function( event, container ) {
container.pointer.on( 'click', '[data-action="dismiss"]', function() {
container.element.pointer( 'close' );
} );
}
};

$( '#' + config.targetId ).pointer( options ).pointer( 'open' );
}

$( initPointer );
}
)( window.jQuery, window.wp, { ...document.currentScript.dataset } );
JS
,
array(
'id' => $slug,
'data-slug' => $pointer->get_slug(),
'data-class' => implode( ' ', $class ),
'data-target-id' => $pointer->get_target_id(),
'data-title' => wp_kses( $pointer->get_title(), $kses_title ),
'data-content' => wp_kses( $content, $kses_content ),
'data-position' => wp_json_encode( $pointer->get_position() ),
Comment on lines +188 to +193
Copy link
Collaborator

Choose a reason for hiding this comment

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

These will all be escaped as attributes automatically so no escaping is needed here, but the values should still be sanitized/cleaned with wp_kses where relevant. Most importantly, no content is injected directly into JS anymore.

)
);
}
Expand Down
9 changes: 7 additions & 2 deletions includes/Core/Dashboard_Sharing/Dashboard_Sharing.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@

namespace Google\Site_Kit\Core\Dashboard_Sharing;

use Google\Site_Kit\Context;

/**
* Class for handling Dashboard Sharing.
*
Expand All @@ -31,9 +33,12 @@ class Dashboard_Sharing {
*
* @since 1.82.0
* @since 1.158.0 Remove $user_options and $context params.
* @since n.e.x.t Restore $context param.
*
* @param Context $context Plugin context.
*/
public function __construct() {
$this->view_only_pointer = new View_Only_Pointer();
public function __construct( Context $context ) {
$this->view_only_pointer = new View_Only_Pointer( $context );
}

/**
Expand Down
Loading
Loading