Skip to content

Commit 9f865ef

Browse files
committed
Merge branch 'develop' into enhancement/12015-rrm-setup-success-policy-violations-followup.
2 parents e50003c + d8aa38a commit 9f865ef

20 files changed

Lines changed: 489 additions & 116 deletions

assets/js/components/email-reporting/UserSettingsSelectionPanel/Notices.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,13 @@ import { Fragment } from '@wordpress/element';
2727
import AnalyticsDisconnectedNotice from '@/js/components/email-reporting/notices/AnalyticsDisconnectedNotice';
2828
import EmailReportingDisabledNotice from '@/js/components/email-reporting/notices/EmailReportingDisabledNotice';
2929
import EmailReportingDisabledViewOnlyNotice from '@/js/components/email-reporting/notices/EmailReportingDisabledViewOnlyNotice';
30+
import EmailReportingErrorNotice from '@/js/components/email-reporting/notices/EmailReportingErrorNotice';
3031
import SetupAnalyticsNotice from '@/js/components/email-reporting/notices/SetupAnalyticsNotice';
3132

3233
export default function Notices() {
3334
return (
3435
<Fragment>
36+
<EmailReportingErrorNotice />
3537
<AnalyticsDisconnectedNotice />
3638
<SetupAnalyticsNotice />
3739
<EmailReportingDisabledNotice />

assets/js/components/email-reporting/UserSettingsSelectionPanel/index.js

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@
2020
* WordPress dependencies
2121
*/
2222
import { __ } from '@wordpress/i18n';
23-
import { useCallback, useState } from '@wordpress/element';
23+
import { useCallback, useEffect, useState } from '@wordpress/element';
24+
import { usePrevious } from '@wordpress/compose';
2425

2526
/**
2627
* Internal dependencies
@@ -40,6 +41,7 @@ export default function UserSettingsSelectionPanel() {
4041
const isOpen = useSelect( ( select ) =>
4142
select( CORE_UI ).getValue( USER_SETTINGS_SELECTION_PANEL_OPENED_KEY )
4243
);
44+
const previousIsOpen = usePrevious( isOpen );
4345

4446
const onSideSheetOpen = useCallback( () => {
4547
trackEvent(
@@ -173,6 +175,12 @@ export default function UserSettingsSelectionPanel() {
173175

174176
const onNoticeDismiss = useCallback( () => setNotice( null ), [] );
175177

178+
useEffect( () => {
179+
if ( ! previousIsOpen && isOpen ) {
180+
setValue( 'admin-screen-tooltip', { isTooltipVisible: false } );
181+
}
182+
}, [ isOpen, previousIsOpen, setValue ] );
183+
176184
return (
177185
<SelectionPanel
178186
className="googlesitekit-user-settings-selection-panel"

assets/js/components/email-reporting/UserSettingsSelectionPanel/index.stories.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,10 @@ export default {
196196
subscribed: true,
197197
} );
198198

199+
registry
200+
.dispatch( CORE_SITE )
201+
.receiveGetEmailReportingErrors( [] );
202+
199203
registry
200204
.dispatch( CORE_UI )
201205
.setValue( USER_SETTINGS_SELECTION_PANEL_OPENED_KEY, true );

assets/js/components/email-reporting/UserSettingsSelectionPanel/index.test.js

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,31 @@ describe( 'UserSettingsSelectionPanel', () => {
133133
expect( dialog ).toHaveAttribute( 'aria-hidden', 'false' );
134134
} );
135135

136+
it( 'hides admin screen tooltip when the panel opens', async () => {
137+
registry
138+
.dispatch( CORE_UI )
139+
.setValue( USER_SETTINGS_SELECTION_PANEL_OPENED_KEY, false );
140+
registry.dispatch( CORE_UI ).setValue( 'admin-screen-tooltip', {
141+
isTooltipVisible: true,
142+
} );
143+
144+
render( <UserSettingsSelectionPanel />, {
145+
registry,
146+
features,
147+
viewContext: VIEW_CONTEXT_MAIN_DASHBOARD,
148+
} );
149+
150+
registry
151+
.dispatch( CORE_UI )
152+
.setValue( USER_SETTINGS_SELECTION_PANEL_OPENED_KEY, true );
153+
154+
await waitFor( () =>
155+
expect(
156+
registry.select( CORE_UI ).getValue( 'admin-screen-tooltip' )
157+
).toMatchObject( { isTooltipVisible: false } )
158+
);
159+
} );
160+
136161
it( 'calls saveEmailReportingSettings with subscribed true when subscribing', async () => {
137162
const coreUserDispatch = registry.dispatch( CORE_USER );
138163
const saveSpy = jest

assets/js/components/email-reporting/notices/AnalyticsDisconnectedNotice.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ export default function AnalyticsDisconnectedNotice() {
109109

110110
return (
111111
<NoticeWithIntersectionObserver
112-
className="googlesitekit-email-reporting__analytics-disconnected-notice"
112+
className="googlesitekit-email-reporting__admin-settings-notice"
113113
type={ TYPES.WARNING }
114114
title={ __( 'Analytics is disconnected', 'google-site-kit' ) }
115115
description={ description }
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
/**
2+
* EmailReportingErrorNotice component.
3+
*
4+
* Site Kit by Google, Copyright 2026 Google LLC
5+
*
6+
* Licensed under the Apache License, Version 2.0 (the "License");
7+
* you may not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* https://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
19+
/**
20+
* WordPress dependencies
21+
*/
22+
import { __ } from '@wordpress/i18n';
23+
24+
/**
25+
* Internal dependencies
26+
*/
27+
import { useSelect } from 'googlesitekit-data';
28+
import Notice from '@/js/components/Notice';
29+
import { TYPES } from '@/js/components/Notice/constants';
30+
import { CORE_SITE } from '@/js/googlesitekit/datastore/site/constants';
31+
import useViewOnly from '@/js/hooks/useViewOnly';
32+
import useNotificationEvents from '@/js/googlesitekit/notifications/hooks/useNotificationEvents';
33+
import withIntersectionObserver from '@/js/util/withIntersectionObserver';
34+
35+
export const EMAIL_REPORTING_ERROR_NOTICE = 'email_reporting_error_notice';
36+
37+
const NoticeWithIntersectionObserver = withIntersectionObserver( Notice );
38+
39+
export default function EmailReportingErrorNotice() {
40+
const isViewOnly = useViewOnly();
41+
42+
const trackEvents = useNotificationEvents( EMAIL_REPORTING_ERROR_NOTICE );
43+
44+
const isEmailReportingEnabled = useSelect( ( select ) =>
45+
select( CORE_SITE ).isEmailReportingEnabled()
46+
);
47+
48+
const emailReportingErrors = useSelect( ( select ) =>
49+
select( CORE_SITE ).getEmailReportingErrors()
50+
);
51+
52+
if (
53+
! isEmailReportingEnabled ||
54+
isViewOnly ||
55+
emailReportingErrors?.length === 0
56+
) {
57+
return null;
58+
}
59+
60+
return (
61+
<NoticeWithIntersectionObserver
62+
className="googlesitekit-email-reporting__admin-settings-notice"
63+
type={ TYPES.ERROR }
64+
title={ __( 'Email reports are paused', 'google-site-kit' ) }
65+
description={ __(
66+
'We were unable to deliver your report. Report delivery will automatically resume once the issue is resolved.',
67+
'google-site-kit'
68+
) }
69+
onInView={ trackEvents.view }
70+
/>
71+
);
72+
}
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
/**
2+
* EmailReportingErrorNotice component tests.
3+
*
4+
* Site Kit by Google, Copyright 2026 Google LLC
5+
*
6+
* Licensed under the Apache License, Version 2.0 (the "License");
7+
* you may not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* https://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
19+
/**
20+
* Internal dependencies
21+
*/
22+
23+
import EmailReportingErrorNotice from './EmailReportingErrorNotice';
24+
import {
25+
render,
26+
createTestRegistry,
27+
provideUserAuthentication,
28+
} from '../../../../../tests/js/test-utils';
29+
import { CORE_SITE } from '@/js/googlesitekit/datastore/site/constants';
30+
import * as tracking from '@/js/util/tracking';
31+
import { VIEW_CONTEXT_MAIN_DASHBOARD_VIEW_ONLY } from '@/js/googlesitekit/constants';
32+
33+
const mockTrackEvent = jest.spyOn( tracking, 'trackEvent' );
34+
mockTrackEvent.mockImplementation( () => Promise.resolve() );
35+
36+
describe( 'EmailReportingErrorNotice', () => {
37+
let registry;
38+
39+
beforeEach( () => {
40+
registry = createTestRegistry();
41+
provideUserAuthentication( registry );
42+
mockTrackEvent.mockClear();
43+
} );
44+
45+
it( 'should render the error notice when email reporting is enabled, user is not view-only, and there are errors', () => {
46+
registry.dispatch( CORE_SITE ).receiveGetEmailReportingSettings( {
47+
enabled: true,
48+
} );
49+
registry.dispatch( CORE_SITE ).receiveGetEmailReportingErrors( {
50+
errors: [ { code: 'test_error' } ],
51+
errorData: [],
52+
} );
53+
54+
const { container, getByText } = render(
55+
<EmailReportingErrorNotice />,
56+
{
57+
registry,
58+
}
59+
);
60+
61+
expect( container ).not.toBeEmptyDOMElement();
62+
expect( getByText( 'Email reports are paused' ) ).toBeInTheDocument();
63+
expect(
64+
getByText(
65+
'We were unable to deliver your report. Report delivery will automatically resume once the issue is resolved.'
66+
)
67+
).toBeInTheDocument();
68+
} );
69+
70+
it( 'should not render when email reporting is not enabled', () => {
71+
registry.dispatch( CORE_SITE ).receiveGetEmailReportingSettings( {
72+
enabled: false,
73+
} );
74+
registry.dispatch( CORE_SITE ).receiveGetEmailReportingErrors( {
75+
errors: [ { code: 'test_error' } ],
76+
errorData: [],
77+
} );
78+
79+
const { container } = render( <EmailReportingErrorNotice />, {
80+
registry,
81+
} );
82+
83+
expect( container ).toBeEmptyDOMElement();
84+
} );
85+
86+
it( 'should not render when dashboard context is view-only', () => {
87+
registry.dispatch( CORE_SITE ).receiveGetEmailReportingSettings( {
88+
enabled: true,
89+
} );
90+
registry.dispatch( CORE_SITE ).receiveGetEmailReportingErrors( {
91+
errors: [ { code: 'test_error' } ],
92+
errorData: [],
93+
} );
94+
95+
const { container } = render( <EmailReportingErrorNotice />, {
96+
registry,
97+
viewContext: VIEW_CONTEXT_MAIN_DASHBOARD_VIEW_ONLY,
98+
} );
99+
expect( container ).toBeEmptyDOMElement();
100+
} );
101+
102+
it( 'should not render when there are no errors', () => {
103+
registry.dispatch( CORE_SITE ).receiveGetEmailReportingSettings( {
104+
enabled: true,
105+
} );
106+
registry.dispatch( CORE_SITE ).receiveGetEmailReportingErrors( [] );
107+
108+
const { container } = render( <EmailReportingErrorNotice />, {
109+
registry,
110+
} );
111+
112+
expect( container ).toBeEmptyDOMElement();
113+
} );
114+
} );

assets/js/components/settings/SettingsCardEmailReporting.test.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ describe( 'SettingsCardEmailReporting', () => {
4242

4343
// Prevent network request/resolver from running to avoid console errors.
4444
registry.dispatch( CORE_USER ).receiveGetDismissedItems( [] );
45+
46+
registry.dispatch( CORE_SITE ).receiveGetEmailReportingErrors( [] );
4547
} );
4648

4749
it( 'should render the layout with correct title', async () => {

assets/js/components/settings/SettingsEmailReporting.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ import EmailReportingCardNotice, {
4545
import AnalyticsDisconnectedNotice from '@/js/components/email-reporting/notices/AnalyticsDisconnectedNotice';
4646
import useViewContext from '@/js/hooks/useViewContext';
4747
import { trackEvent } from '@/js/util';
48+
import EmailReportingErrorNotice from '@/js/components/email-reporting/notices/EmailReportingErrorNotice';
4849

4950
export default function SettingsEmailReporting( { loading = false } ) {
5051
const viewContext = useViewContext();
@@ -169,6 +170,7 @@ export default function SettingsEmailReporting( { loading = false } ) {
169170
</Cell>
170171
</Row>
171172
) }
173+
<EmailReportingErrorNotice />
172174
<AnalyticsDisconnectedNotice />
173175
</Fragment>
174176
);

assets/js/components/settings/SettingsEmailReporting.test.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,8 @@ describe( 'SettingsEmailReporting', () => {
5050
// Prevent network request/resolver from running to avoid console errors.
5151
registry.dispatch( CORE_USER ).receiveGetDismissedItems( [] );
5252

53+
registry.dispatch( CORE_SITE ).receiveGetEmailReportingErrors( [] );
54+
5355
mockTrackEvent.mockClear();
5456
} );
5557

0 commit comments

Comments
 (0)