Skip to content

Commit

Permalink
Added click tracking for recommendations helper (TryGhost#18496)
Browse files Browse the repository at this point in the history
  • Loading branch information
SimonBackx authored Oct 5, 2023
1 parent d454f18 commit 1882b70
Show file tree
Hide file tree
Showing 4 changed files with 53 additions and 11 deletions.
30 changes: 30 additions & 0 deletions apps/portal/src/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,7 @@ export default class App extends React.Component {
});
}
}
this.setupRecommendationButtons();
} catch (e) {
/* eslint-disable no-console */
console.error(`[Portal] Failed to initialize:`, e);
Expand Down Expand Up @@ -888,6 +889,35 @@ export default class App extends React.Component {
};
}

getRecommendationButtons() {
const customTriggerSelector = '[data-recommendation]';
return document.querySelectorAll(customTriggerSelector) || [];
}

/** Setup click tracking for recommendation buttons */
setupRecommendationButtons() {
// Handler for custom buttons
const clickHandler = (event) => {
// Send beacons for recommendation clicks
const recommendationId = event.currentTarget.dataset.recommendation;

if (recommendationId) {
this.dispatchAction('trackRecommendationClicked', {
recommendationId
// eslint-disable-next-line no-console
}).catch(console.error);
} else {
// eslint-disable-next-line no-console
console.warn('[Portal] Invalid usage of data-recommendation attribute');
}
};

const elements = this.getRecommendationButtons();
for (const element of elements) {
element.addEventListener('click', clickHandler, {passive: true});
}
}

render() {
if (this.state.initStatus === 'success') {
return (
Expand Down
12 changes: 12 additions & 0 deletions apps/portal/src/actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -502,6 +502,18 @@ async function oneClickSubscribe({data: {siteUrl}, state}) {
}

function trackRecommendationClicked({data: {recommendationId}, api}) {
try {
const existing = localStorage.getItem('ghost-recommendations-clicked');
const clicked = existing ? JSON.parse(existing) : [];
if (clicked.includes(recommendationId)) {
// Already tracked
return;
}
clicked.push(recommendationId);
localStorage.setItem('ghost-recommendations-clicked', JSON.stringify(clicked));
} catch (e) {
// Ignore localstorage errors (browser not supported or in private mode)
}
api.recommendations.trackClicked({
recommendationId
});
Expand Down
2 changes: 1 addition & 1 deletion ghost/core/core/frontend/helpers/tpl/recommendations.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<ul class="recommendations">
{{#each recommendations as |rec|}}
<li class="recommendation">
<a href="{{rec.url}}">
<a href="{{rec.url}}" data-recommendation="{{rec.id}}" target="_blank" rel="noopener">
<img class="recommendation-favicon" src="{{rec.favicon}}" alt="{{rec.title}}">
<h5 class="recommendation-title">{{rec.title}}</h5>
<p class="recommendation-reason">{{rec.reason}}</p>
Expand Down
20 changes: 10 additions & 10 deletions ghost/core/test/unit/frontend/helpers/recommendations.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,11 @@ describe('{{#recommendations}} helper', function () {
sinon.stub(api, 'recommendationsPublic').get(() => {
return {
browse: sinon.stub().resolves({recommendations: [
{title: 'Recommendation 1', url: 'https://recommendations1.com', favicon: 'https://recommendations1.com/favicon.ico', reason: 'Reason 1'},
{title: 'Recommendation 2', url: 'https://recommendations2.com', favicon: 'https://recommendations2.com/favicon.ico', reason: 'Reason 2'},
{title: 'Recommendation 3', url: 'https://recommendations3.com', favicon: 'https://recommendations3.com/favicon.ico', reason: 'Reason 3'},
{title: 'Recommendation 4', url: 'https://recommendations4.com', favicon: 'https://recommendations4.com/favicon.ico', reason: 'Reason 4'},
{title: 'Recommendation 5', url: 'https://recommendations5.com', favicon: 'https://recommendations5.com/favicon.ico', reason: 'Reason 5'}
{id: '1', title: 'Recommendation 1', url: 'https://recommendations1.com', favicon: 'https://recommendations1.com/favicon.ico', reason: 'Reason 1'},
{id: '2', title: 'Recommendation 2', url: 'https://recommendations2.com', favicon: 'https://recommendations2.com/favicon.ico', reason: 'Reason 2'},
{id: '3', title: 'Recommendation 3', url: 'https://recommendations3.com', favicon: 'https://recommendations3.com/favicon.ico', reason: 'Reason 3'},
{id: '4', title: 'Recommendation 4', url: 'https://recommendations4.com', favicon: 'https://recommendations4.com/favicon.ico', reason: 'Reason 4'},
{id: '5', title: 'Recommendation 5', url: 'https://recommendations5.com', favicon: 'https://recommendations5.com/favicon.ico', reason: 'Reason 5'}
], meta: meta})
};
});
Expand All @@ -71,35 +71,35 @@ describe('{{#recommendations}} helper', function () {
const expected = trimSpaces(html`
<ul class="recommendations">
<li class="recommendation">
<a href="https://recommendations1.com">
<a href="https://recommendations1.com" data-recommendation="1" target="_blank" rel="noopener">
<img class="recommendation-favicon" src="https://recommendations1.com/favicon.ico" alt="Recommendation 1">
<h5 class="recommendation-title">Recommendation 1</h5>
<p class="recommendation-reason">Reason 1</p>
</a>
</li>
<li class="recommendation">
<a href="https://recommendations2.com">
<a href="https://recommendations2.com" data-recommendation="2" target="_blank" rel="noopener">
<img class="recommendation-favicon" src="https://recommendations2.com/favicon.ico" alt="Recommendation 2">
<h5 class="recommendation-title">Recommendation 2</h5>
<p class="recommendation-reason">Reason 2</p>
</a>
</li>
<li class="recommendation">
<a href="https://recommendations3.com">
<a href="https://recommendations3.com" data-recommendation="3" target="_blank" rel="noopener">
<img class="recommendation-favicon" src="https://recommendations3.com/favicon.ico" alt="Recommendation 3">
<h5 class="recommendation-title">Recommendation 3</h5>
<p class="recommendation-reason">Reason 3</p>
</a>
</li>
<li class="recommendation">
<a href="https://recommendations4.com">
<a href="https://recommendations4.com" data-recommendation="4" target="_blank" rel="noopener">
<img class="recommendation-favicon" src="https://recommendations4.com/favicon.ico" alt="Recommendation 4">
<h5 class="recommendation-title">Recommendation 4</h5>
<p class="recommendation-reason">Reason 4</p>
</a>
</li>
<li class="recommendation">
<a href="https://recommendations5.com">
<a href="https://recommendations5.com" data-recommendation="5" target="_blank" rel="noopener">
<img class="recommendation-favicon" src="https://recommendations5.com/favicon.ico" alt="Recommendation 5">
<h5 class="recommendation-title">Recommendation 5</h5>
<p class="recommendation-reason">Reason 5</p>
Expand Down

0 comments on commit 1882b70

Please sign in to comment.