Repository navigation
Hide Site Goals widget fully with warning notice when no custom dimension data exists within the selected date range #12774
Copy link
Copy link
Closed
Labels
P0High priorityHigh priorityTeam SIssues for Squad 1Issues for Squad 1Type: EnhancementImprovement of an existing featureImprovement of an existing feature
Description
Feature Description
When every event provider plugin for a Site Goals widget has been deactivated and the currently selected dashboard date range contains no conversion events for that widget's goal type, the widget's content should be replaced by a warning notice.
See the "Notice when Event Providers are deactivated and no events detected for the selected date range" section of the Site Goals Enhancements - Mini design doc and the Figma mock.
Acceptance Criteria
- Each Site Goals widget is fully replaced with a warning notice when both of the following are true:
- no event provider plugin for that widget is active and
- the currently selected dashboard date range contains no conversion events for that widget's goal type.
- Any conversion event for the goal type counts, i.e. whether or not Site Kit attributed it to a provider or form. The notice is about the widget having nothing left to report, so it applies equally to a widget in its original aggregated state or in the tabbed breakdown state.
- When the notice renders it is the widget's only content — no tabs, tiles, badges or other notices — so the per-tab notice from Show warning notice in Site Goals widget when event provider plugin is deactivated #12773 cannot appear alongside it.
- The notice is a warning notice with a single "Got it" button as per the Figma mock.
- Clicking "Got it" removes that widget from the dashboard, for all users of the site, until the conditions below restore it.
- If the other widget still qualifies, only the actioned widget is removed — the Site Goals area and the remaining widget stay visible.
- If neither widget qualifies, the entire Site Goals area and its dashboard navigation chip are removed.
- A widget may only be removed while no event provider for it is active. A request to remove a widget whose provider is still active is rejected, so the widget cannot be removed for everyone by calling the API directly.
- Removal is never automatic. No elapsed detection window, date range change or page reload may remove a widget on its own; only the "Got it" click does.
- Site Goals reappears on its own, with no admin action and no new setting, once a supported event provider is active again and events are detected.
- The notice disappears and the widget's normal content returns whenever a condition stops being true — for example the provider is reactivated, or the date range is widened to cover events.
Implementation Brief
PHP
- In
includes/Modules/Analytics_4/Site_Goals_Site_Settings.php:- Add a
remove_widget( $category )method that reads the current value, filters$categoryout ofactiveWidgetsandset()s the result, returning the updated settings. The existingmerge()only ever unionsactiveWidgets(by design, so the cron never drops values), so it cannot be used to remove a category.
- Add a
- Create
includes/Modules/Analytics_4/Datapoints/Remove_Site_Goals_Widget.php:- Extend
Site_Goals_Settings_Datapoint. Removal is deliberately available to any dashboard user, including view-only ones: by the time the notice renders there is no data for the widget to show, so it is already effectively absent, and no destructive data change is involved. The daily cron restoresactiveWidgetson its own once events return. - Take
site_goals_site_settingsfrom the definition in the constructor, exactly asGet_Site_Goals_Settingsdoes, pluscontext(the guard below needs it), following the precedent inGet_Report. - Accept a
widgetrequest param, validating it againstSite_Goals_Site_Settings::ALLOWED_WIDGETS— throwMissing_Required_Param_Exceptionwhen absent andInvalid_Param_Exceptionwhen it is not an allowed slug. - Guard the removal server-side. Before removing anything, call
( new Conversion_Tracking( $this->context ) )->get_active_provider_categories()and return aWP_Errorwith a400status when$widgetis in that list. The endpoint is reachable by any dashboard user, so without this a direct API request could remove the widget for everyone regardless of what the UI shows.get_active_provider_categories()returns the same'lead'/'ecommerce'slugs asALLOWED_WIDGETS, so the check is a singlein_array().- This guard is deliberately the same condition the cron uses in
update_active_site_goals_widgets(), which makes the two impossible to fight: while a provider is active the removal is refused, and once it is inactive the cron will not re-add the widget either. There is no window in which repeated requests could outrun the cron. - The date range half of the criteria is deliberately not enforced here. The date range is user-supplied input, so it cannot act as a guard — any caller could satisfy it by choosing an empty range — and checking it would mean running an Analytics report on every removal request.
- This guard is deliberately the same condition the cron uses in
- Call
remove_widget()and return the site-wide settings (array( 'activeWidgets' => … )), which is all the JS store needs to reconcile its state.
- Extend
- In
includes/Modules/Analytics_4.php:- Register
'POST:remove-site-goals-widget'inget_datapoint_definitions(), alongside the existingPOST:save-site-goals-settingsentry — the same definition arrayGET:site-goals-settingsis given, plus'context' => $this->context. Save_Site_Goals_Settingswrites per-user settings only — leave it untouched;activeWidgetsmust not become writable through it.
- Register
JS datastore
- In
assets/js/modules/analytics-4/datastore/site-goals-settings.ts:- Add a
fetchRemoveSiteGoalsWidgetStoreviacreateFetchStore, posting tomodules/analytics-4/data/remove-site-goals-widgetwith{ widget: category }. - Its
reducerCallbackmerges the response'sactiveWidgetsoverstate.siteGoalsSettings, mirroring howfetchSaveSiteGoalsSettingsStoremerges the per-user fields while preserving the rest. - Add the
removeSiteGoalsWidget( category )action as acreateValidatedAction, followingsaveSiteGoalsSettings: clear the previous action error, dispatch the fetch, callsetErrorForActionon failure, and return{ response, error }. - Add an
isRemovingSiteGoalsWidget( state )selector readingstate.isFetchingRemoveSiteGoalsWidget, mirroring the existingisSavingSiteGoalsSettings. - No other selector changes are needed:
isSiteGoalsWidgetActive()readsactiveWidgetsandisSiteGoalsWidgetRenderable()builds on it, so both flip tofalseonce the category is removed.
- Add a
- No dashboard changes are required to hide the area or the chip. The widget registrations in
assets/js/modules/analytics-4/widgets/index.js(analyticsOnlineStorePerformance/analyticsLeadGenerationPerformance) already gateisActiveonisSiteGoalsWidgetRenderable(), andAREA_MAIN_DASHBOARD_SITE_GOALS_PRIMARY/CONTEXT_MAIN_DASHBOARD_SITE_GOALSare dropped fromDashboardMainAppanduseVisibleSectionswhen no widget in the area is active.
"No events in the selected date range" check
- Add a
useSiteGoalsHasEventsInDateRange( goalType )hook inassets/js/modules/analytics-4/components/site-goals/hooks/:- Build report options from
select( CORE_USER ).getDateRangeDates( { compare: false } )withmetrics: [ { name: 'eventCount' } ],dimensionFilters: { eventName: { filterType: 'inListFilter', value: <goal type's events> } }and a dedicatedreportID, resolving the events fromCONVERSION_REPORTING_ECOMMERCE_EVENTS/CONVERSION_REPORTING_LEAD_EVENTS. - No
dimensionsare needed — the report's single totals row is the whole answer. Read it withisZeroReport()from@/js/modules/analytics-4/utils/is-zero-report. - Fetch through
useInViewSelect, matchinguseSiteGoalsBreakdown. Returnundefinedwhile the report is loading or has errored, otherwise! isZeroReport( report ).
- Build report options from
Component
- Create
assets/js/modules/analytics-4/components/site-goals/components/SiteGoalsRemovalNotice.tsx, alongside the existingEventProviderDeactivatedNotice.tsx:- Props:
goalType. - Render
<Notice>appropriately as per the Figma. - Resolve the per-goal-type title and description through a co-located
useSiteGoalsRemovalNoticeCopy( goalType )hook, mirroring theuseEventProviderDeactivatedNoticeCopypattern that Show warning notice in Site Goals widget when event provider plugin is deactivated #12773 established. onClickdispatchesremoveSiteGoalsWidget( goalType ), takesinProgressfromisRemovingSiteGoalsWidget(), and fires atrackEvent( `${ viewContext }_site-goals-widget`, 'remove_widget', goalType )event.
- Props:
Widgets
- In
assets/js/modules/analytics-4/components/site-goals/widgets/OnlineStorePerformanceWidget.tsxandLeadGenerationPerformanceWidget.tsx:- Select
hasActiveEcommerceEventProviders/hasActiveLeadEventProvidersfromCORE_SITEand calluseSiteGoalsHasEventsInDateRange( GOAL_TYPES.ECOMMERCE | GOAL_TYPES.LEAD ). - Derive
shouldShowRemovalNotice = hasActiveProviders === false && hasEventsInDateRange === false. - Return early if
shouldShowRemovalNotice, rendering the<SiteGoalsRemovalNotice goalType={ … } />. Don't return or wrap the WidgetComponent as it adds widget styling we don't need.
- Select
Test Coverage
- Add tests and stories for the new
SiteGoalsRemovalNoticecomponent and its copy hook, covering both goal types and the "Got it" click. - Add tests for the new hook that checks whether the selected date range has any events for a goal type, including its loading and error states.
- Extend the
site-goals-settingsdatastore tests to cover the new removal action and the resulting widget state. - Extend the
OnlineStorePerformanceWidgetandLeadGenerationPerformanceWidgettests and stories to cover when the notice replaces the widget and when it does not, in both the aggregated and tabbed breakdown states. - Extend the widget registration tests to confirm a removed widget, and the Site Goals area, stop rendering.
- Add PHPUnit coverage for removing a widget from the site-wide Site Goals settings, for the new REST datapoint including its params, permissions and its refusal to remove a widget whose event provider is still active, and for the cron restoring a widget that was previously removed.
QA Brief
- Use a site whose Analytics property has online store and lead events. Deactivate every online store plugin (WooCommerce, Easy Digital Downloads), and pick a date range with no online store events. Confirm a warning notice replaces the whole "Online store performance" widget and matches the Figma mock. The "Learn more" link sits inline at the end of the text, not on a new line as the mock shows, as agreed in Slack.
- Reload with the page scrolled to the top, then scroll down to "Site goals". Confirm the "Online store performance" card shows its title and one grey loading block until the notice takes its place, not a bare grey block without the card.
- Switch between date ranges with no online store events. Confirm the notice stays in each.
- Narrow the browser to a phone width and confirm the notice stays readable, with nothing cut off.
- Sign in as a view-only (Dashboard Sharing) user and confirm they see the same notice with its Got it button.
- Widen the date range to one that holds online store events. Confirm the notice leaves and the widget shows its tiles.
- Go back to a range with no online store events, activate an online store plugin and reload. Confirm the notice leaves and the "Online store performance" widget shows its content.
- Deactivate the online store plugin again, run this in the console, then reload. Confirm the Site Goals intro modal reads See what brings in new leads, not Track your most valuable goals:
googlesitekit.data.dispatch( 'core/user' ).removeDismissedItems( 'site-goals-intro-modal-banner' );- Click Show me and confirm the tour holds two steps, with the first one pointing at the "Lead generation performance" widget.
- Click Got it on the online store notice. Confirm only that notice leaves the page, and the "Lead generation performance" widget stays.
- Sign in as the view-only user again and confirm the "Online store performance" widget is gone for them too.
- Deactivate every form plugin. Confirm a matching notice replaces the "Lead generation performance" widget.
- Run the console line above again and reload. Confirm the intro modal does not appear, since no Site Goals widget shows its content.
- Click Got it on the lead generation notice, then reload. Confirm the dashboard no longer shows the "Site goals" section or its chip at the top.
- Activate an online store plugin and run the daily conversion reporting cron. Reload and confirm the "Site goals" chip and the "Online store performance" widget come back, with the widget's content.
- Confirm the intro modal now reads See what drives your sales, since its dismissal is still cleared.
- With the online store plugin still active, run this in the console. Confirm the Network tab shows the
remove-site-goals-widgetrequest answered with400, and the widget stays on the dashboard:
googlesitekit.api.set( 'modules', 'analytics-4', 'remove-site-goals-widget', { widget: 'ecommerce' } ).catch( () => {} );- Run this in the console to show the notice the way a page opened before the plugin came back would, then click Got it. Confirm the error under the notice reads Error: This Site Goals widget can’t be removed while a plugin that tracks its events is active. with no (Please try again.), and Got it stays clickable:
googlesitekit.data.dispatch( 'core/site' ).receiveSiteInfo( { ...googlesitekit.data.select( 'core/site' ).getSiteInfo(), hasActiveEcommerceEventProviders: false } );Changelog entry
- Add a warning notice that replaces a Site Goals widget when none of its event provider plugins is active and the selected date range has no conversion events, with an option to remove the widget.
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
P0High priorityHigh priorityTeam SIssues for Squad 1Issues for Squad 1Type: EnhancementImprovement of an existing featureImprovement of an existing feature
Activity
[-]Hide Site Goals widget and show dismissible warning notice when no custom dimension data exists within the selected date range[/-][+]Hide Site Goals widget fully with warning notice when no custom dimension data exists within the selected date range[/+]eugene-manuilov commentedon May 22, 2026
AC ✔️
47 remaining items
kelvinballoo commentedon Oct 6, 2026
QA Update⚠️
THis is mostly verified good.
I just have one clarification:
What do you think?
shervElmi commentedon Oct 7, 2026
Hi @kelvinballoo, thanks for the QA update.
What you saw is what the AC asks for. Clicking Got it removes the widget "for all users of the site." A view-only user can click it too. @jimmymadon gives the reasons in the comment on why any dashboard user can click Got it.
But I agree that an administrator should see the notice. It says that reinstalling the plugin resumes the tracking. Only an administrator can reinstall a plugin. So I think only an administrator should be able to remove the widget. A view-only user would still see the notice, without the Got it button.
@jimmymadon, what do you think?
jimmymadon commentedon Oct 7, 2026
@shervElmi @kelvinballoo
Thanks for this - these are valid concerns. However, this is a very rare and unusual edge case. To prevent view-only users from actioning this adds more code to have separate permissions for this endpoint, so I don't think it is worth the extra effort and complexity here.
Maybe a mid-way "hack" would be to simply hide the button on the front-end for view only users (but they can still hit the api endpoint). Additionally, we've made sure that any user can't simply hide the widget - the associated event provider plugin would really have to be deactivated and custom dimension data for the date range would be totally empty.
Rather than introducing any anomaly, I would say this is fine the way it is because:
shervElmi commentedon Oct 7, 2026
Thanks @jimmymadon for the detailed clarification here.
I see your point and I agree. @kelvinballoo I'm moving this one back to QA.
kelvinballoo commentedon Oct 8, 2026
QA Update ✅
This was verified good. Moving to approval.
Online store has the notice, and it's responsive at different breakpoints.
Reloading the page doesn't make the notice go away. As long as other date ranges do not have data, the notice remains.
Details
12774.-.01.-.Online.store.has.the.notice.mov
12774.-.01.-.reponsive.different.breakpoints.mov
12774.-.02.-.reload.page.mov
12774.-.03.-.different.ranges.mov
View only user works as expected. Once notice is dismissed, it's dismissed across the site for other users as well. This works from admin and also view-only user. The view-only case was discussed in this ticket and was deemed ok.
Details
12774.-.05.-.Dismissed.view-dahsbaord.admin.also.dismissed.not.ok.480p.mov
12774.-.08.-.dismiss.in.admin.and.view.dismissed.as.well.mov
Reactivating the store plugin brought back the widget and the proper site goals modal appears as expected.
Details
12774.-.06.-.reactivating.store.plugin.ok.mov
12774.-.07.-.bring.up.new.leads.modal.ok.mov
When forms plugins were deactivated, no modal appeared in that case. (No store modal as well).
Details
12774.-.10.-.forms.deactivated.-.no.moal.site.goal.ok.mov
12774.-.11.-.no.chip.ok.mov
When we have no data in the last 28 days, the notice appears without the widget. When we change to 90 days where we have data, the widget appears as expected.
Details
12774.-.DC.retest.01.mov
12774.-.DC.test.2.ok.90.day.and.28.day.ok.mov