Skip to content

Commit 6b54b03

Browse files
authored
Stats: navigate by full periods (#99140)
* navigate by full periods * allow range navigation up to 3 years * no need to add another param for shortcut id * fix today * fix ending date after today for next arrow
1 parent 8863e5c commit 6b54b03

File tree

2 files changed

+91
-12
lines changed

2 files changed

+91
-12
lines changed

client/my-sites/stats/hooks/use-moment-site-zone.ts

+4-1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import moment from 'moment';
44
import { useSelector } from 'calypso/state';
55
import { getSiteOption } from 'calypso/state/sites/selectors';
66
import { getSelectedSiteId } from 'calypso/state/ui/selectors';
7+
import { DATE_FORMAT } from '../constants';
78

89
export const getMomentSiteZone = createSelector(
910
( state: object, siteId: number | null ) => {
@@ -16,7 +17,9 @@ export const getMomentSiteZone = createSelector(
1617

1718
const gmtOffset = getSiteOption( state, siteId, 'gmt_offset' ) as number;
1819
if ( Number.isFinite( gmtOffset ) ) {
19-
return localizedMoment.utcOffset( gmtOffset );
20+
// In all the components, `moment` is directly used, which defaults to the browser's local timezone.
21+
// As a result, we need to convert the moment object to the site's timezone for easier comparison like `isSame`.
22+
return moment( localizedMoment.utcOffset( gmtOffset ).format( DATE_FORMAT ) );
2023
}
2124

2225
// Falls back to the browser's local timezone if no GMT offset is found

client/my-sites/stats/stats-period-navigation/index.jsx

+87-11
Original file line numberDiff line numberDiff line change
@@ -139,8 +139,60 @@ class StatsPeriodNavigation extends PureComponent {
139139
this.handleArrowNavigation( false );
140140
};
141141

142+
// TODO: refactor to extract logic with `dateForCustomRange` from `client/my-sites/stats/stats-date-picker/index.jsx`.
143+
getFullPeriod = () => {
144+
const { moment, dateRange, momentSiteZone } = this.props;
145+
const localizedStartDate = moment( dateRange.chartStart );
146+
const localizedEndDate = moment( dateRange.chartEnd );
147+
148+
// If it's a partial month but ends today.
149+
if (
150+
localizedStartDate.isSame( localizedStartDate.clone().startOf( 'month' ), 'day' ) &&
151+
localizedEndDate.isSame( momentSiteZone, 'day' ) &&
152+
localizedStartDate.isSame( localizedEndDate, 'month' ) &&
153+
( ! dateRange.shortcutId || dateRange.shortcutId === 'month_to_date' )
154+
) {
155+
return 'month';
156+
}
157+
158+
// If it's a partial year but ends today.
159+
if (
160+
localizedStartDate.isSame( localizedStartDate.clone().startOf( 'year' ), 'day' ) &&
161+
localizedEndDate.isSame( momentSiteZone, 'day' ) &&
162+
localizedStartDate.isSame( localizedEndDate, 'year' ) &&
163+
( ! dateRange.shortcutId || dateRange.shortcutId === 'year_to_date' )
164+
) {
165+
return 'year';
166+
}
167+
168+
// If it's the same day, show single date.
169+
if ( localizedStartDate.isSame( localizedEndDate, 'day' ) ) {
170+
return 'day';
171+
}
172+
173+
// If it's a full month.
174+
if (
175+
localizedStartDate.isSame( localizedStartDate.clone().startOf( 'month' ), 'day' ) &&
176+
localizedEndDate.isSame( localizedEndDate.clone().endOf( 'month' ), 'day' ) &&
177+
localizedStartDate.isSame( localizedEndDate, 'month' )
178+
) {
179+
return 'month';
180+
}
181+
182+
// If it's a full year.
183+
if (
184+
localizedStartDate.isSame( localizedStartDate.clone().startOf( 'year' ), 'day' ) &&
185+
localizedEndDate.isSame( localizedEndDate.clone().endOf( 'year' ), 'day' ) &&
186+
localizedStartDate.isSame( localizedEndDate, 'year' )
187+
) {
188+
return 'year';
189+
}
190+
191+
return null;
192+
};
193+
142194
handleArrowNavigation = ( previousOrNext = false ) => {
143-
const { moment, period, slug, dateRange } = this.props;
195+
const { moment, momentSiteZone, period, slug, dateRange } = this.props;
144196

145197
const isWPAdmin = config.isEnabled( 'is_odyssey' );
146198
const event_from = isWPAdmin ? 'jetpack_odyssey' : 'calypso';
@@ -150,16 +202,39 @@ class StatsPeriodNavigation extends PureComponent {
150202
} );
151203

152204
const navigationStart = moment( dateRange.chartStart );
153-
const navigationEnd = moment( dateRange.chartEnd );
154-
155-
if ( previousOrNext ) {
156-
// Navigate to the previous date range.
157-
navigationStart.subtract( dateRange.daysInRange, 'days' );
158-
navigationEnd.subtract( dateRange.daysInRange, 'days' );
205+
let navigationEnd = moment( dateRange.chartEnd );
206+
207+
// If it's a full month then we need to navigate to the previous/next month.
208+
// If it's a full year then we need to navigate to the previous/next year.
209+
210+
const fullPeriod = this.getFullPeriod();
211+
if ( ! fullPeriod ) {
212+
// Usual flow - only based on the days in range.
213+
if ( previousOrNext ) {
214+
// Navigate to the previous date range.
215+
navigationStart.subtract( dateRange.daysInRange, 'days' );
216+
navigationEnd.subtract( dateRange.daysInRange, 'days' );
217+
} else {
218+
// Navigate to the next date range.
219+
navigationStart.add( dateRange.daysInRange, 'days' );
220+
navigationEnd.add( dateRange.daysInRange, 'days' );
221+
}
159222
} else {
160-
// Navigate to the next date range.
161-
navigationStart.add( dateRange.daysInRange, 'days' );
162-
navigationEnd.add( dateRange.daysInRange, 'days' );
223+
// Navigate range by full periods.
224+
if ( previousOrNext ) {
225+
// Navigate to the previous period.
226+
navigationStart.subtract( 1, fullPeriod );
227+
navigationEnd.subtract( 1, fullPeriod );
228+
} else {
229+
// Navigate to the next period.
230+
navigationStart.add( 1, fullPeriod );
231+
navigationEnd.add( 1, fullPeriod );
232+
}
233+
navigationStart.startOf( fullPeriod );
234+
navigationEnd.endOf( fullPeriod );
235+
if ( navigationEnd.isAfter( momentSiteZone, 'day' ) ) {
236+
navigationEnd = momentSiteZone;
237+
}
163238
}
164239

165240
const chartStart = navigationStart.format( 'YYYY-MM-DD' );
@@ -246,7 +321,8 @@ class StatsPeriodNavigation extends PureComponent {
246321
momentSiteZone,
247322
'day'
248323
);
249-
const showArrowsForDateRange = showArrows && dateRange?.daysInRange <= 31;
324+
// Make sure we only show arrows for date ranges that are less than 3 years for performance reasons.
325+
const showArrowsForDateRange = showArrows && dateRange?.daysInRange <= 365 * 3;
250326

251327
return (
252328
<div

0 commit comments

Comments
 (0)