diff --git a/assets/js/components/email-reporting/FrequencySelector.stories.js b/assets/js/components/email-reporting/FrequencySelector.stories.js
index ca4bfdf2355..0558c57088f 100644
--- a/assets/js/components/email-reporting/FrequencySelector.stories.js
+++ b/assets/js/components/email-reporting/FrequencySelector.stories.js
@@ -41,6 +41,7 @@ export default {
},
args: {
isUserSubscribed: false,
+ referenceDate: '2026-07-14',
savedSettings: {},
clientSettings: {
frequency: 'weekly',
@@ -50,7 +51,12 @@ export default {
},
};
-function Template( { isUserSubscribed, savedSettings, clientSettings } ) {
+function Template( {
+ isUserSubscribed,
+ savedSettings,
+ clientSettings,
+ referenceDate,
+} ) {
function setupRegistry( registry ) {
provideSiteInfo( registry, {
startOfWeek: savedSettings?.startOfWeek,
@@ -63,6 +69,8 @@ function Template( { isUserSubscribed, savedSettings, clientSettings } ) {
registry
.dispatch( CORE_USER )
.setEmailReportingSettings( clientSettings );
+
+ registry.dispatch( CORE_USER ).setReferenceDate( referenceDate );
}
return (
@@ -113,6 +121,7 @@ WeeklySelectedSundayStartOfTheWeek.scenario = {};
export const PreviouslySavedFrequency = Template.bind( {} );
PreviouslySavedFrequency.args = {
isUserSubscribed: true,
+ referenceDate: '2026-07-14',
savedSettings: {
frequency: 'monthly',
},
diff --git a/assets/js/components/email-reporting/FrequencySelector.test.js b/assets/js/components/email-reporting/FrequencySelector.test.js
index a81ab7cacc0..b3256eede0f 100644
--- a/assets/js/components/email-reporting/FrequencySelector.test.js
+++ b/assets/js/components/email-reporting/FrequencySelector.test.js
@@ -27,7 +27,7 @@ import FrequencySelector from './FrequencySelector';
function setupRegistry(
registry,
- { startOfWeek = 1, frequency, savedFrequency } = {}
+ { startOfWeek = 1, frequency, savedFrequency, referenceDate } = {}
) {
provideSiteInfo( registry, { startOfWeek } );
@@ -44,6 +44,10 @@ function setupRegistry(
if ( frequency ) {
registry.dispatch( CORE_USER ).setEmailReportingFrequency( frequency );
}
+
+ if ( referenceDate ) {
+ registry.dispatch( CORE_USER ).setReferenceDate( referenceDate );
+ }
}
function renderSelector( registry, props = {} ) {
@@ -66,6 +70,7 @@ describe( 'FrequencySelector', () => {
beforeEach( () => {
registry = createTestRegistry();
+ global.innerWidth = 1024;
} );
describe( 'Story states (visual + DOM)', () => {
@@ -114,6 +119,7 @@ describe( 'FrequencySelector', () => {
startOfWeek: 1,
frequency: 'weekly',
savedFrequency: 'monthly',
+ referenceDate: '2026-07-14',
} );
const { container, containerElement, getByText } = renderSelector(
@@ -142,6 +148,9 @@ describe( 'FrequencySelector', () => {
// Check that the pill text is correct.
expect( getByText( 'Current subscription' ) ).toBeInTheDocument();
+ expect(
+ getByText( 'Next report: Aug 1, 2026' )
+ ).toBeInTheDocument();
expect( containerElement ).toMatchSnapshot();
} );
@@ -151,6 +160,7 @@ describe( 'FrequencySelector', () => {
startOfWeek: 1,
frequency: 'monthly',
savedFrequency: 'monthly',
+ referenceDate: '2026-07-14',
} );
const { container, containerElement, getByText } = renderSelector(
@@ -182,9 +192,41 @@ describe( 'FrequencySelector', () => {
)
).toBe( true );
expect( monthlyCard.getAttribute( 'aria-checked' ) ).toBe( 'true' );
+ expect(
+ getByText( 'Next report: Aug 1, 2026' )
+ ).toBeInTheDocument();
expect( containerElement ).toMatchSnapshot();
} );
+
+ it( 'Renders next report date in the mobile current subscription pill', () => {
+ global.innerWidth = 500;
+
+ setupRegistry( registry, {
+ startOfWeek: 1,
+ frequency: 'weekly',
+ savedFrequency: 'monthly',
+ referenceDate: '2026-07-14',
+ } );
+
+ const { container, getByText } = renderSelector( registry, {
+ isUserSubscribed: true,
+ } );
+
+ expect(
+ container.querySelector(
+ '.googlesitekit-frequency-selector__badge-row'
+ )
+ ).not.toBeInTheDocument();
+ expect(
+ container.querySelector(
+ '.googlesitekit-frequency-selector__current-subscription'
+ )
+ ).toBeInTheDocument();
+ expect(
+ getByText( 'Next report: Aug 1, 2026' )
+ ).toBeInTheDocument();
+ } );
} );
describe( 'Interactions', () => {
diff --git a/assets/js/components/email-reporting/getNextReportDate.test.ts b/assets/js/components/email-reporting/getNextReportDate.test.ts
new file mode 100644
index 00000000000..f87361b11da
--- /dev/null
+++ b/assets/js/components/email-reporting/getNextReportDate.test.ts
@@ -0,0 +1,89 @@
+/**
+ * Get next report date tests.
+ *
+ * Site Kit by Google, Copyright 2026 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.
+ */
+
+/**
+ * Internal dependencies
+ */
+import { getDateString } from '@/js/util';
+import getNextReportDate from './getNextReportDate';
+
+describe( 'getNextReportDate', () => {
+ describe( 'weekly frequency', () => {
+ it.each( [
+ [ '2026-07-14', 1, '2026-07-20' ], // Tuesday -> next Monday.
+ [ '2026-07-12', 0, '2026-07-19' ], // Sunday -> next Sunday.
+ [ '2026-07-18', 6, '2026-07-25' ], // Saturday -> next Saturday.
+ ] )(
+ 'returns %s week start %i as %s',
+ ( referenceDate, weekStartDay, expectedDate ) => {
+ const nextReportDate = getNextReportDate(
+ 'weekly',
+ referenceDate,
+ weekStartDay
+ );
+
+ expect( getDateString( nextReportDate ) ).toEqual(
+ expectedDate
+ );
+ }
+ );
+ } );
+
+ describe( 'monthly frequency', () => {
+ it.each( [
+ [ '2026-01-31', '2026-02-01' ],
+ [ '2026-07-14', '2026-08-01' ],
+ [ '2026-12-31', '2027-01-01' ],
+ ] )(
+ 'returns next month first day for %s as %s',
+ ( referenceDate, expectedDate ) => {
+ const nextReportDate = getNextReportDate(
+ 'monthly',
+ referenceDate,
+ 1
+ );
+
+ expect( getDateString( nextReportDate ) ).toEqual(
+ expectedDate
+ );
+ }
+ );
+ } );
+
+ describe( 'quarterly frequency', () => {
+ it.each( [
+ [ '2026-01-15', '2026-04-01' ],
+ [ '2026-03-31', '2026-04-01' ],
+ [ '2026-11-20', '2027-01-01' ],
+ [ '2026-12-31', '2027-01-01' ],
+ ] )(
+ 'returns next quarter first day for %s as %s',
+ ( referenceDate, expectedDate ) => {
+ const nextReportDate = getNextReportDate(
+ 'quarterly',
+ referenceDate,
+ 1
+ );
+
+ expect( getDateString( nextReportDate ) ).toEqual(
+ expectedDate
+ );
+ }
+ );
+ } );
+} );
diff --git a/assets/js/components/email-reporting/getNextReportDate.ts b/assets/js/components/email-reporting/getNextReportDate.ts
new file mode 100644
index 00000000000..e22a53b5d99
--- /dev/null
+++ b/assets/js/components/email-reporting/getNextReportDate.ts
@@ -0,0 +1,88 @@
+/**
+ * Utility to compute the next email report date.
+ *
+ * Site Kit by Google, Copyright 2026 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.
+ */
+
+/**
+ * Internal dependencies
+ */
+import { stringToDate } from '@/js/util';
+
+type EmailReportingFrequency = 'weekly' | 'monthly' | 'quarterly';
+
+/**
+ * Gets the next report date for a given frequency.
+ *
+ * @since n.e.x.t
+ *
+ * @param {EmailReportingFrequency} frequency Email reporting frequency.
+ * @param {string} referenceDate Date string as `YYYY-MM-DD`.
+ * @param {number} weekStartDay Week start day index (`0` for Sunday through `6` for Saturday).
+ * @return {Date} Next report date.
+ */
+export default function getNextReportDate(
+ frequency: EmailReportingFrequency,
+ referenceDate: string,
+ weekStartDay: number
+): Date {
+ const nextReportDate = stringToDate( referenceDate );
+
+ switch ( frequency ) {
+ case 'weekly': {
+ const normalizedWeekStartDay =
+ Number.isInteger( weekStartDay ) &&
+ weekStartDay >= 0 &&
+ weekStartDay <= 6
+ ? weekStartDay
+ : 1;
+ const currentWeekday = nextReportDate.getDay();
+ let daysUntilTarget =
+ ( normalizedWeekStartDay - currentWeekday + 7 ) % 7;
+
+ // Use the next occurrence of the week start day.
+ if ( 0 === daysUntilTarget ) {
+ daysUntilTarget = 7;
+ }
+
+ nextReportDate.setDate(
+ nextReportDate.getDate() + daysUntilTarget
+ );
+ break;
+ }
+
+ case 'monthly':
+ nextReportDate.setMonth( nextReportDate.getMonth() + 1, 1 );
+ break;
+
+ case 'quarterly': {
+ const currentMonth = nextReportDate.getMonth() + 1;
+ // Month offset within quarter: 0 => first, 1 => second, 2 => third.
+ const position = ( currentMonth - 1 ) % 3;
+ const monthsToAdd = 3 - position;
+
+ nextReportDate.setMonth(
+ nextReportDate.getMonth() + monthsToAdd,
+ 1
+ );
+ break;
+ }
+
+ default:
+ break;
+ }
+
+ return nextReportDate;
+}
diff --git a/assets/sass/components/email-reporting/_googlesitekit-frequency-selector.scss b/assets/sass/components/email-reporting/_googlesitekit-frequency-selector.scss
index 21ddff6a614..bdea80bad4b 100644
--- a/assets/sass/components/email-reporting/_googlesitekit-frequency-selector.scss
+++ b/assets/sass/components/email-reporting/_googlesitekit-frequency-selector.scss
@@ -31,8 +31,11 @@ h3.googlesitekit-frequency-selector-title {
background-color: $c-surfaces-surface;
border-radius: 4px;
color: $c-surfaces-on-surface-variant;
+ display: flex;
+ flex-direction: column;
+ gap: 2px;
margin-bottom: $grid-gap-phone;
- padding: 4px 0;
+ padding: 8px 4px;
text-align: center;
width: 100%;
@@ -43,6 +46,11 @@ h3.googlesitekit-frequency-selector-title {
}
}
+// Increase specificity to override body-small typography letter spacing.
+.googlesitekit-typography.googlesitekit-typography--body.googlesitekit-typography--small.googlesitekit-frequency-selector__next-report {
+ letter-spacing: initial;
+}
+
.googlesitekit-frequency-selector__current-subscription--selected {
background-color: $c-site-kit-sk-10;
color: $c-site-kit-sk-500;