Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Top recent trading pages #7710

Merged
merged 23 commits into from
Oct 18, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
be32be3
Add new widget to key-metrics-widgets list.
zutigrm Oct 9, 2023
129c614
Add KM_ANALYTICS_TOP_RECENT_TRENDING_PAGES const.
zutigrm Oct 9, 2023
953152c
Add TopRecentTrendingPages widget.
zutigrm Oct 9, 2023
bc8e74d
Add stories file.
zutigrm Oct 9, 2023
f9c9374
Fix In_List_Filter dimension value.
zutigrm Oct 17, 2023
44fe2c7
Include Top recent trending pages widget in the list.
zutigrm Oct 17, 2023
60f2c84
Add rows to the metric tile.
zutigrm Oct 17, 2023
bf959ad
Change vlaues to value in dimensionFilters.
zutigrm Oct 17, 2023
a2b14c6
Move top recent trending pages under newsKeyMetric flag.
zutigrm Oct 17, 2023
0fe5b48
Update validate shared dimensions with custom dimension.
zutigrm Oct 17, 2023
8f5f9f4
Sync with develop.
zutigrm Oct 17, 2023
3891d00
Remove unused widget included with sync.
zutigrm Oct 17, 2023
5b0f3f3
Move reportOptions into function so it can be used in HOC.
zutigrm Oct 17, 2023
65c0154
Update stories.
zutigrm Oct 17, 2023
970353f
Update VRT images.
zutigrm Oct 17, 2023
e36b7af
Rename to TopRecentTrendingPagesWidget.
zutigrm Oct 18, 2023
e2a6ccb
Extract report options into separate file.
zutigrm Oct 18, 2023
178b307
Update report options and dates to re-use function and renamed compon…
zutigrm Oct 18, 2023
a8debe9
Use report options from file and component rename.
zutigrm Oct 18, 2023
0eb8339
Update VRT to adjust to component name change.
zutigrm Oct 18, 2023
b60c50c
Resolve merge conflict.
zutigrm Oct 18, 2023
a90b482
Fix failing test.
zutigrm Oct 18, 2023
643d395
Use correct date values in widget.
tofumatt Oct 18, 2023
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
Expand Up @@ -24,25 +24,221 @@ import PropTypes from 'prop-types';
/**
* WordPress dependencies
*/
import { __ } from '@wordpress/i18n';
import { compose } from '@wordpress/compose';

/**
* Internal dependencies
*/
import Data from 'googlesitekit-data';
import { MODULES_ANALYTICS_4 } from '../../datastore/constants';
import { KEY_METRICS_WIDGETS } from '../../../../components/KeyMetrics/key-metrics-widgets';
import { KM_ANALYTICS_TOP_RECENT_TRENDING_PAGES } from '../../../../googlesitekit/datastore/user/constants';
import { MetricTileTable } from '../../../../components/KeyMetrics';
import Link from '../../../../components/Link';
import { ZeroDataMessage } from '../../../analytics/components/common';
import { getDateString, getPreviousDate, numFmt } from '../../../../util';
import {
MetricTileTable,
MetricTileTablePlainText,
} from '../../../../components/KeyMetrics';
import whenActive from '../../../../util/when-active';
import withCustomDimensions from '../../utils/withCustomDimensions';
import ConnectGA4CTATileWidget from './ConnectGA4CTATileWidget';
import useViewOnly from '../../../../hooks/useViewOnly';
import withCustomDimensions from '../../utils/withCustomDimensions';
const { useSelect, useInViewSelect } = Data;

/**
* Computes the dates for the last three days relative to today.
*
* Utilizing the current date, the function calculates the dates
* for the previous day, two days ago, and three days ago.
*
* @since n.e.x.t
*
* @return {Object} An object containing the dates for yesterday,
* two days ago, and three days ago.
*/
export const getDates = () => {
const today = new Date();
const todayDateString = getDateString( today );

const yesterday = getPreviousDate( todayDateString, 1 );
const twoDaysAgo = getPreviousDate( todayDateString, 2 );
const threeDaysAgo = getPreviousDate( todayDateString, 3 );

return {
yesterday,
twoDaysAgo,
threeDaysAgo,
};
};

/**
* Returns the date range (eg. the `startDate` and `endDate`) for this widget's
* reporting options.
*
* @since n.e.x.t
*
* @return {Object} An object containing the `startDate` and `endDate` for a
* report.
*/
export const getDateRange = () => {
const { yesterday, threeDaysAgo } = getDates();

return {
startDate: threeDaysAgo,
endDate: yesterday,
};
};

/**
* Generates the report options required for fetching data in
* `topRecentTrendingPagesWidget`.
*
* The function utilizes the dates from the last three days to
* prepare the filter and structure required for the report.
*
* This is defined as a function outside the component so that both
* the component and the higher-order-component (`withCustomDimensions`)
* can use it.
*
* @since n.e.x.t
*
* @return {Object} The report options containing dimensions, filters,
* metrics, and other parameters.
*/
export const getReportOptions = () => {
const { yesterday, twoDaysAgo, threeDaysAgo } = getDates();

const dates = getDateRange();

const reportOptions = {
...dates,
dimensions: [ 'pagePath' ],
dimensionFilters: {
'customEvent:googlesitekit_post_date': {
filterType: 'inListFilter',
value: [
yesterday.replace( /-/g, '' ),
twoDaysAgo.replace( /-/g, '' ),
threeDaysAgo.replace( /-/g, '' ),
],
},
},
metrics: [ { name: 'screenPageViews' } ],
orderby: [
{
metric: {
metricName: 'screenPageViews',
},
desc: true,
},
],
limit: 3,
};

return reportOptions;
};

function TopRecentTrendingPagesWidget( { Widget } ) {
const viewOnlyDashboard = useViewOnly();

const dates = getDateRange();

const reportOptions = getReportOptions();

const report = useInViewSelect( ( select ) =>
select( MODULES_ANALYTICS_4 ).getReport( reportOptions )
);

const error = useSelect( ( select ) =>
select( MODULES_ANALYTICS_4 ).getErrorForSelector( 'getReport', [
reportOptions,
] )
);

const titles = useInViewSelect( ( select ) =>
! error && report
? select( MODULES_ANALYTICS_4 ).getPageTitles(
report,
reportOptions
)
: undefined
);

const loading = useSelect(
( select ) =>
! select( MODULES_ANALYTICS_4 ).hasFinishedResolution(
'getReport',
[ reportOptions ]
) || titles === undefined
);

const { rows = [] } = report || {};

const columns = [
{
field: 'dimensionValues.0.value',
Component: ( { fieldValue } ) => {
const url = fieldValue;
const title = titles[ url ];
// Utilizing `useSelect` inside the component rather than
// returning its direct value to the `columns` array.
// This pattern ensures that the component re-renders correctly based on changes in state,
// preventing potential issues with stale or out-of-sync data.
// Note: This pattern is replicated in a few other spots within our codebase.
const serviceURL = useSelect( ( select ) => {
return ! viewOnlyDashboard
? select( MODULES_ANALYTICS_4 ).getServiceReportURL(
'all-pages-and-screens',
{
filters: {
unifiedPagePathScreen: url,
},
dates,
}
)
: null;
} );

if ( viewOnlyDashboard ) {
return <MetricTileTablePlainText content={ title } />;
}

return (
<Link
href={ serviceURL }
title={ title }
external
hideExternalIndicator
>
{ title }
</Link>
);
},
},
{
field: 'metricValues.0.value',
Component: ( { fieldValue } ) => (
<strong>{ numFmt( fieldValue ) }</strong>
),
},
];

return (
<MetricTileTable
widgetSlug={ KM_ANALYTICS_TOP_RECENT_TRENDING_PAGES }
Widget={ Widget }
title={ __( 'Top recent trending pages', 'google-site-kit' ) }
loading={ loading }
rows={ rows }
columns={ columns }
ZeroState={ ZeroDataMessage }
error={ error }
moduleSlug="analytics-4"
infoTooltip={ __(
'Pages with the most pageviews published in the last 3 days',
'google-site-kit'
) }
/>
);
}
Expand All @@ -56,5 +252,10 @@ export default compose(
moduleName: 'analytics-4',
FallbackComponent: ConnectGA4CTATileWidget,
} ),
withCustomDimensions()
withCustomDimensions( {
dimensions:
KEY_METRICS_WIDGETS[ KM_ANALYTICS_TOP_RECENT_TRENDING_PAGES ]
.requiredCustomDimensions?.[ 0 ],
reportOptions: getReportOptions(),
} )
)( TopRecentTrendingPagesWidget );
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,40 @@ import { KM_ANALYTICS_TOP_RECENT_TRENDING_PAGES } from '../../../../googlesiteki
import { provideModules } from '../../../../../../tests/js/utils';
import { withWidgetComponentProps } from '../../../../googlesitekit/widgets/util';
import WithRegistrySetup from '../../../../../../tests/js/WithRegistrySetup';
import TopRecentTrendingPagesWidget from './TopRecentTrendingPagesWidget';
import TopRecentTrendingPagesWidget, {
getDateRange,
getReportOptions,
} from './TopRecentTrendingPagesWidget';
import { provideCustomDimensionError } from '../../utils/custom-dimensions';
import {
STRATEGY_ZIP,
getAnalytics4MockResponse,
provideAnalytics4MockReport,
} from '../../utils/data-mock';
import { replaceValuesInAnalytics4ReportWithZeroData } from '../../../../../../.storybook/utils/zeroReports';

const WidgetWithComponentProps = withWidgetComponentProps(
KM_ANALYTICS_TOP_RECENT_TRENDING_PAGES
)( TopRecentTrendingPagesWidget );

const reportOptions = getReportOptions();

const dateRange = getDateRange();

const pageTitlesReportOptions = {
...dateRange,
dimensionFilters: {
pagePath: new Array( 3 )
.fill( '' )
.map( ( _, i ) => `/test-post-${ i + 1 }/` )
.sort(),
},
dimensions: [ 'pagePath', 'pageTitle' ],
metrics: [ { name: 'screenPageViews' } ],
orderby: [ { metric: { metricName: 'screenPageViews' }, desc: true } ],
limit: 15,
};

const Template = ( { setupRegistry, ...args } ) => (
<WithRegistrySetup func={ setupRegistry }>
<WidgetWithComponentProps { ...args } />
Expand All @@ -50,11 +77,120 @@ Ready.args = {
.requiredCustomDimensions?.[ 0 ],
],
} );

const pageTitlesReport = getAnalytics4MockResponse(
pageTitlesReportOptions,
// Use the zip combination strategy to ensure a one-to-one mapping of page paths to page titles.
// Otherwise, by using the default cartesian product of dimension values, the resulting output will have non-matching
// page paths to page titles.
{ dimensionCombinationStrategy: STRATEGY_ZIP }
);

registry
.dispatch( MODULES_ANALYTICS_4 )
.receiveGetReport( pageTitlesReport, {
options: pageTitlesReportOptions,
} );

provideAnalytics4MockReport( registry, reportOptions );
},
};
Ready.parameters = {
features: [ 'newsKeyMetrics' ],
};
Ready.scenario = {
label: 'KeyMetrics/TopRecentTrendingPagesWidget/Ready',
};

export const Loading = Template.bind( {} );
Loading.storyName = 'Loading';
Loading.args = {
setupRegistry: ( { dispatch } ) => {
dispatch( MODULES_ANALYTICS_4 ).setSettings( {
propertyID: '12345',
availableCustomDimensions: [
KEY_METRICS_WIDGETS[ KM_ANALYTICS_TOP_RECENT_TRENDING_PAGES ]
.requiredCustomDimensions?.[ 0 ],
],
} );

dispatch( MODULES_ANALYTICS_4 ).startResolution( 'getReport', [
reportOptions,
] );
},
};
Loading.parameters = {
features: [ 'newsKeyMetrics' ],
};
Loading.scenario = {
label: 'KeyMetrics/TopRecentTrendingPagesWidget/Loading',
};

export const ZeroData = Template.bind( {} );
ZeroData.storyName = 'Zero Data';
ZeroData.args = {
setupRegistry: ( { dispatch } ) => {
const report = getAnalytics4MockResponse( reportOptions );
const zeroReport =
replaceValuesInAnalytics4ReportWithZeroData( report );

dispatch( MODULES_ANALYTICS_4 ).receiveGetReport( zeroReport, {
options: reportOptions,
} );
dispatch( MODULES_ANALYTICS_4 ).setSettings( {
propertyID: '12345',
availableCustomDimensions: [
KEY_METRICS_WIDGETS[ KM_ANALYTICS_TOP_RECENT_TRENDING_PAGES ]
.requiredCustomDimensions?.[ 0 ],
],
} );
},
};
ZeroData.scenario = {
label: 'KeyMetrics/TopRecentTrendingPagesWidget/ZeroData',
};

export const Error = Template.bind( {} );
Error.storyName = 'Error';
Error.args = {
setupRegistry: ( { dispatch } ) => {
const errorObject = {
code: 400,
message: 'Test error message. ',
data: {
status: 400,
reason: 'badRequest',
},
selectorData: {
storeName: 'modules/analytics-4',
name: 'getReport',
args: [ reportOptions ],
},
};

dispatch( MODULES_ANALYTICS_4 ).receiveError(
errorObject,
'getReport',
[ reportOptions ]
);

dispatch( MODULES_ANALYTICS_4 ).finishResolution( 'getReport', [
reportOptions,
] );

dispatch( MODULES_ANALYTICS_4 ).setSettings( {
propertyID: '12345',
availableCustomDimensions: [
KEY_METRICS_WIDGETS[ KM_ANALYTICS_TOP_RECENT_TRENDING_PAGES ]
.requiredCustomDimensions?.[ 0 ],
],
} );
},
};
Error.scenario = {
label: 'KeyMetrics/TopRecentTrendingPagesWidget/Error',
delay: 250,
};

export const ErrorMissingCustomDimensions = Template.bind( {} );
ErrorMissingCustomDimensions.storyName = 'Error - Missing custom dimensions';
Expand Down
Loading
Loading