Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
088c28a
Add a new setting to track if analytics was connected.
jimmymadon Nov 24, 2025
0ada451
Expose the setting at a new core site endpoint.
jimmymadon Nov 24, 2025
92409c2
Update constructor signature of REST controller.
jimmymadon Nov 24, 2025
3e5bb7c
Set the analytics was connected flag to true when analytics is deacti…
jimmymadon Nov 24, 2025
962cd7b
Add a simple fetch store to access the was analytics connected endpoint.
jimmymadon Nov 24, 2025
a7d1b4a
Add a selector to access the analytics 4 was connected setting.
jimmymadon Nov 24, 2025
555e05e
Add a resolver for the was analytics 4 connected setting.
jimmymadon Nov 24, 2025
efc0c1e
Add a notice when analytics is disconnected but was connected once.
jimmymadon Nov 24, 2025
09f114e
Use the new notice in Settings Admin.
jimmymadon Nov 24, 2025
8664721
Style the notice.
jimmymadon Nov 24, 2025
392adaf
Update dismissed item ID.
jimmymadon Nov 24, 2025
2677434
Check for email reporting enabled state instead of user subscription …
jimmymadon Nov 24, 2025
bd7827e
Fix selector.
jimmymadon Nov 24, 2025
7575fdd
Add tests for the analytics disconnected notice.
jimmymadon Nov 24, 2025
023e314
Fix existing tests.
jimmymadon Nov 24, 2025
748adca
Fix other existing tests.
jimmymadon Nov 24, 2025
7df48f8
Update constructor signature in PHPUnit.
jimmymadon Nov 24, 2025
f7218ad
Add REST route test.
jimmymadon Nov 24, 2025
d09f3ff
Update release version to be next.
jimmymadon Nov 24, 2025
3e286ab
Add tests for new resolver and selector.
jimmymadon Nov 24, 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
@@ -0,0 +1,99 @@
/**
* AnalyticsDisconnectedNotice component.
*
* 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.
*/

/**
* WordPress dependencies
*/
import { __ } from '@wordpress/i18n';
import { useCallback } from '@wordpress/element';

/**
* Internal dependencies
*/
import { useDispatch, useSelect } from 'googlesitekit-data';
import { CORE_USER } from '@/js/googlesitekit/datastore/user/constants';
import Notice from '@/js/components/Notice';
import { TYPES } from '@/js/components/Notice/constants';
import { CORE_SITE } from '@/js/googlesitekit/datastore/site/constants';
import { CORE_MODULES } from '@/js/googlesitekit/modules/datastore/constants';
import { MODULE_SLUG_ANALYTICS_4 } from '@/js/modules/analytics-4/constants';
import useActivateModuleCallback from '@/js/hooks/useActivateModuleCallback';

export const EMAIL_REPORTING_ANALYTICS_DISCONNECTED_NOTICE_DISMISSED_ITEM =
'email-reporting-analytics-disconnected-notice';

export default function AnalyticsDisconnectedNotice() {
const isEmailReportingEnabled = useSelect( ( select ) =>
select( CORE_SITE ).isEmailReportingEnabled()
);

const isAnalyticsConnected = useSelect( ( select ) =>
select( CORE_MODULES ).isModuleConnected( MODULE_SLUG_ANALYTICS_4 )
);

const wasAnalyticsConnected = useSelect( ( select ) =>
select( CORE_SITE ).getWasAnalytics4Connected()
);

const isDismissed = useSelect( ( select ) =>
select( CORE_USER ).isItemDismissed(
EMAIL_REPORTING_ANALYTICS_DISCONNECTED_NOTICE_DISMISSED_ITEM
)
);

const { dismissItem } = useDispatch( CORE_USER );

const activateAnalytics = useActivateModuleCallback(
MODULE_SLUG_ANALYTICS_4
);

const handleDismiss = useCallback( async () => {
await dismissItem(
EMAIL_REPORTING_ANALYTICS_DISCONNECTED_NOTICE_DISMISSED_ITEM
);
}, [ dismissItem ] );

if (
! isEmailReportingEnabled ||
isDismissed !== false ||
isAnalyticsConnected ||
! wasAnalyticsConnected
) {
return null;
}

return (
<Notice
className="googlesitekit-email-reporting__analytics-disconnected-notice"
type={ TYPES.WARNING }
title={ __( 'Analytics is disconnected', 'google-site-kit' ) }
description={ __(
'Email reports won’t include Analytics data and metrics',
'google-site-kit'
) }
ctaButton={ {
label: __( 'Connect Analytics', 'google-site-kit' ),
onClick: activateAnalytics,
} }
dismissButton={ {
label: __( 'Got it', 'google-site-kit' ),
onClick: handleDismiss,
} }
/>
);
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

We can complete the coverage by including two more cases to verify that notice is not showing in these scenarios:

  • does not render when Analytics is active
  • does not render if Analytics was once connected

Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
/**
* AnalyticsDisconnectedNotice component tests.
*
* 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 { waitFor } from '@testing-library/react';

/**
* Internal dependencies
*/
import AnalyticsDisconnectedNotice, {
EMAIL_REPORTING_ANALYTICS_DISCONNECTED_NOTICE_DISMISSED_ITEM,
} from './AnalyticsDisconnectedNotice';
import {
createTestRegistry,
render,
fireEvent,
provideModules,
provideUserCapabilities,
provideUserAuthentication,
provideModuleRegistrations,
provideSiteInfo,
} from '../../../../tests/js/test-utils';
import { CORE_USER } from '@/js/googlesitekit/datastore/user/constants';
import { MODULE_SLUG_ANALYTICS_4 } from '@/js/modules/analytics-4/constants';
import { CORE_SITE } from '@/js/googlesitekit/datastore/site/constants';
import { mockLocation } from '../../../../tests/js/mock-browser-utils';

describe( 'AnalyticsDisconnectedNotice', () => {
mockLocation();
let registry;

const fetchDismissItem = new RegExp(
'^/google-site-kit/v1/core/user/data/dismiss-item'
);
const fetchGetDismissedItems = new RegExp(
'^/google-site-kit/v1/core/user/data/dismissed-items'
);

beforeEach( () => {
registry = createTestRegistry();
provideSiteInfo( registry );
provideUserAuthentication( registry );
provideUserCapabilities( registry );
provideModules( registry, [
{
slug: MODULE_SLUG_ANALYTICS_4,
active: false,
connected: false,
},
] );
provideModuleRegistrations( registry );

registry.dispatch( CORE_USER ).receiveGetDismissedItems( [] );
registry.dispatch( CORE_SITE ).receiveGetEmailReportingSettings( {
enabled: true,
} );
registry.dispatch( CORE_SITE ).receiveGetWasAnalytics4Connected( true );
} );

it( 'renders the notice when email reporting is enabled, analytics is disconnected but was once connected and notice is not dismissed', () => {
const { getByText } = render( <AnalyticsDisconnectedNotice />, {
registry,
} );

// Title and description should be present.
expect( getByText( /Analytics is disconnected/i ) ).toBeInTheDocument();
expect(
getByText(
/Email reports won’t include Analytics data and metrics/i
)
).toBeInTheDocument();
} );

it( 'renders the "Reconnect Analytics" button and activates the module on click', async () => {
const moduleActivationEndpoint = RegExp(
'google-site-kit/v1/core/modules/data/activation'
);

const userAuthenticationEndpoint = RegExp(
'^/google-site-kit/v1/core/user/data/authentication'
);

fetchMock.getOnce( userAuthenticationEndpoint, {
body: { needsReauthentication: false },
} );

fetchMock.postOnce( moduleActivationEndpoint, {
body: { success: true },
} );

const { getByRole } = render( <AnalyticsDisconnectedNotice />, {
registry,
} );

fireEvent.click(
getByRole( 'button', {
name: /connect analytics/i,
} )
);

await waitFor( () =>
expect( fetchMock ).toHaveFetched( moduleActivationEndpoint )
);
} );

it( 'dismisses the notice when "Got it" is clicked', async () => {
fetchMock.getOnce( fetchGetDismissedItems, { body: [] } );
fetchMock.postOnce( fetchDismissItem, {
body: [
EMAIL_REPORTING_ANALYTICS_DISCONNECTED_NOTICE_DISMISSED_ITEM,
],
} );

const { getByRole } = render( <AnalyticsDisconnectedNotice />, {
registry,
} );

fireEvent.click( getByRole( 'button', { name: /got it/i } ) );

await waitFor( () =>
expect( fetchMock ).toHaveFetched( fetchDismissItem )
);
} );

it( 'does not render when email reporting is disabled', () => {
registry.dispatch( CORE_SITE ).receiveGetEmailReportingSettings( {
enabled: false,
} );

const { container } = render( <AnalyticsDisconnectedNotice />, {
registry,
} );

expect( container ).toBeEmptyDOMElement();
} );

it( 'does not render when notice is dismissed', () => {
registry
.dispatch( CORE_USER )
.receiveGetDismissedItems( [
EMAIL_REPORTING_ANALYTICS_DISCONNECTED_NOTICE_DISMISSED_ITEM,
] );

const { container } = render( <AnalyticsDisconnectedNotice />, {
registry,
} );

expect( container ).toBeEmptyDOMElement();
} );
} );
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ import {
createTestRegistry,
provideUserAuthentication,
freezeFetch,
provideModules,
provideUserCapabilities,
} from '../../../../tests/js/utils';
import { CORE_SITE } from '@/js/googlesitekit/datastore/site/constants';
import SettingsCardEmailReporting from './SettingsCardEmailReporting';
Expand All @@ -35,9 +37,12 @@ describe( 'SettingsCardEmailReporting', () => {
beforeEach( () => {
registry = createTestRegistry();
provideUserAuthentication( registry );
provideUserCapabilities( registry );
provideModules( registry );

// Prevent network request/resolver from running to avoid console errors.
registry.dispatch( CORE_USER ).receiveGetDismissedItems( [] );
registry.dispatch( CORE_SITE ).receiveGetWasAnalytics4Connected( true );
} );

it( 'should render the layout with correct title', async () => {
Expand Down
2 changes: 2 additions & 0 deletions assets/js/components/settings/SettingsEmailReporting.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ import Typography from '@/js/components/Typography';
import EmailReportingCardNotice, {
EMAIL_REPORTING_CARD_NOTICE_DISMISSED_ITEM,
} from '@/js/components/email-reporting/EmailReportingCardNotice';
import AnalyticsDisconnectedNotice from '@/js/components/email-reporting/AnalyticsDisconnectedNotice';

export default function SettingsEmailReporting( { loading = false } ) {
const isEnabled = useSelect( ( select ) =>
Expand Down Expand Up @@ -145,6 +146,7 @@ export default function SettingsEmailReporting( { loading = false } ) {
</Cell>
</Row>
) }
<AnalyticsDisconnectedNotice />
</Fragment>
);
}
Expand Down
5 changes: 5 additions & 0 deletions assets/js/components/settings/SettingsEmailReporting.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ import {
createTestRegistry,
provideUserAuthentication,
freezeFetch,
provideUserCapabilities,
provideModules,
} from '../../../../tests/js/utils';
import { CORE_USER } from '@/js/googlesitekit/datastore/user/constants';
import { CORE_SITE } from '@/js/googlesitekit/datastore/site/constants';
Expand All @@ -37,9 +39,12 @@ describe( 'SettingsEmailReporting', () => {
beforeEach( () => {
registry = createTestRegistry();
provideUserAuthentication( registry );
provideUserCapabilities( registry );
provideModules( registry );

// Prevent network request/resolver from running to avoid console errors.
registry.dispatch( CORE_USER ).receiveGetDismissedItems( [] );
registry.dispatch( CORE_SITE ).receiveGetWasAnalytics4Connected( true );
} );

it( 'should render the toggle with correct label', () => {
Expand Down
34 changes: 34 additions & 0 deletions assets/js/googlesitekit/datastore/site/email-reporting.js
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,16 @@ const fetchSaveEmailReportingSettingsStore = createFetchStore( {
},
} );

const fetchGetWasAnalytics4Connected = createFetchStore( {
baseName: 'getWasAnalytics4Connected',
controlCallback: () => {
return get( 'core', 'site', 'was-analytics-4-connected', undefined );
},
reducerCallback: createReducer( ( state, wasAnalytics4Connected ) => {
state.emailReporting.wasAnalytics4Connected = wasAnalytics4Connected;
} ),
} );

// Actions
const SET_EMAIL_REPORTING_SETTINGS = 'SET_EMAIL_REPORTING_SETTINGS';

Expand Down Expand Up @@ -150,6 +160,17 @@ const baseResolvers = {
yield fetchGetEmailReportingSettingsStore.actions.fetchGetEmailReportingSettings();
}
},
*getWasAnalytics4Connected() {
const registry = yield commonActions.getRegistry();

const wasAnalytics4Connected = registry
.select( CORE_SITE )
.getWasAnalytics4Connected();

if ( wasAnalytics4Connected === undefined ) {
yield fetchGetWasAnalytics4Connected.actions.fetchGetWasAnalytics4Connected();
}
},
};

const baseSelectors = {
Expand Down Expand Up @@ -177,11 +198,24 @@ const baseSelectors = {
const settings = state.emailReporting?.settings;
return !! settings?.enabled;
},

/**
* Gets whether Analytics 4 was ever connected.
*
* @since n.e.x.t
*
* @param {Object} state Data store's state.
* @return {(boolean|undefined)} TRUE if Analytics 4 was connected, FALSE if not, or `undefined` if not loaded yet.
*/
getWasAnalytics4Connected( state ) {
return state.emailReporting?.wasAnalytics4Connected;
},
};

const store = combineStores(
fetchGetEmailReportingSettingsStore,
fetchSaveEmailReportingSettingsStore,
fetchGetWasAnalytics4Connected,
{
initialState: baseInitialState,
actions: baseActions,
Expand Down
Loading
Loading