Skip to content

Commit

Permalink
Add right column and mobile sticky ads to football fixtures pages (#1379
Browse files Browse the repository at this point in the history
)

* Add advert in right of desktop football pages

* add football fixtures pages to rules for mobile sticky ad slot

* fix and update tests for mobile sticky conditions

* move fixture page detection to commercial features constructor

* add optional chaining to avoid issues in unrelated tests

* Revert "move fixture page detection to commercial features constructor"

This reverts commit d9e4e16.

* move fixture page detection to commercial features constructor, add page id to initial Jest setup config to prevent test issues

* add changeset

---------

Co-authored-by: Dominik Lander <dominik.lander@guardian.co.uk>
  • Loading branch information
i-hardy and domlander committed May 22, 2024
1 parent 0ad79c0 commit e1b6032
Show file tree
Hide file tree
Showing 11 changed files with 131 additions and 2 deletions.
5 changes: 5 additions & 0 deletions .changeset/eleven-dots-behave.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@guardian/commercial': major
---

add new football-right slot and show ads on fixture pages
1 change: 1 addition & 0 deletions jest.setupTestFrameworkScriptFile.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ window.guardian = {
page: {
idApiUrl: 'https://idapi.theguardian.com',
idUrl: 'https://profile.theguardian.com',
pageId: 'uk-politics',
},
images: {
commercial: {},
Expand Down
9 changes: 9 additions & 0 deletions src/core/ad-sizes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ type SlotName =
| 'merchandising-high'
| 'merchandising'
| 'mobile-sticky'
| 'football-right'
| 'mostpop'
| 'right'
| 'sponsor-logo'
Expand Down Expand Up @@ -396,6 +397,14 @@ const slotSizeMappings = {
'crossword-banner': {
phablet: [adSizes.outOfPage, adSizes.empty, adSizes.leaderboard],
},
'football-right': {
desktop: [
adSizes.empty,
adSizes.mpu,
adSizes.skyscraper,
adSizes.halfPage,
],
},
'article-end': {
mobile: [], // Mappings are dynamically added for this slot using additionalSizes
},
Expand Down
1 change: 1 addition & 0 deletions src/core/create-ad-slot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ type SlotName =
| 'comments'
| 'crossword-banner-mobile'
| 'exclusion'
| 'football-right'
| 'fronts-banner'
| 'merchandising-high'
| 'im'
Expand Down
2 changes: 2 additions & 0 deletions src/init/consented/dynamic-ad-slots.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { init as initArticleAsideAdverts } from 'insert/article-aside-adverts';
import { initCommentsExpandedAdverts } from 'insert/comments-expanded-advert';
import { init as initFootballRightAds } from 'insert/fixures';
import { init as initHighMerch } from 'insert/high-merch';
import { init as initMobileCrosswordsAdvert } from 'insert/mobile-crossword-banner';
import { init as initMobileSticky } from 'insert/mobile-sticky';
Expand All @@ -17,6 +18,7 @@ const dynamicAdSlotModules: Modules = [
['cm-liveblogAdverts', initLiveblogAdverts],
['cm-commentsExpandedAdverts', initCommentsExpandedAdverts],
['cm-crosswordBannerMobile', initMobileCrosswordsAdvert],
['cm-footballRight', initFootballRightAds],
];

export const initDynamicAdSlots = async (): Promise<void> => {
Expand Down
73 changes: 73 additions & 0 deletions src/insert/fixures.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import { AD_LABEL_HEIGHT } from 'core/constants';
import { createAdSlot, wrapSlotInContainer } from 'core/create-ad-slot';
import { commercialFeatures } from 'lib/commercial-features';
import { getBreakpoint } from 'lib/detect/detect-breakpoint';
import { getViewport } from 'lib/detect/detect-viewport';
import fastdom from '../utils/fastdom-promise';
import { fillDynamicAdSlot } from './fill-dynamic-advert-slot';

const LARGEST_AD_SIZE = 600; // px, double mpu
const SPACING = 10; // px, above ad

const insertFootballRightAd = (anchor: HTMLElement) => {
const slot = createAdSlot('football-right');

const container = wrapSlotInContainer(slot, {
className: 'ad-slot-container football-right-ad-container',
});

/**
* TODO: Move these to a class in frontend
*/
container.style.position = 'sticky';
container.style.top = '0';
container.style.marginTop = '10px';

void fastdom
.mutate(() => {
anchor.style.height = '100%';
anchor.appendChild(container);
})
.then(() => fillDynamicAdSlot(slot, false));
};

/**
* Inserts an advert on certain football fixtures/results/tables
* pages in the right column.
*/
export const init = (): Promise<void> => {
if (window.guardian.config.isDotcomRendering) {
return Promise.resolve();
}

const currentBreakpoint = getBreakpoint(getViewport().width);
if (currentBreakpoint !== 'desktop' && currentBreakpoint !== 'wide') {
return Promise.resolve();
}

if (!commercialFeatures.footballFixturesAdverts) {
return Promise.resolve();
}

/**
* On Football pages, this right-hand column exists in the DOM but does not
* appear to be used. Can we use it for an advert?
*/
const anchor: HTMLElement | null = document.querySelector(
'.content__secondary-column',
);

const minSpaceForAd = LARGEST_AD_SIZE + AD_LABEL_HEIGHT + SPACING;

if (
anchor === null ||
anchor.parentElement === null ||
anchor.parentElement.offsetHeight < minSpaceForAd
) {
return Promise.resolve();
}

insertFootballRightAd(anchor);

return Promise.resolve();
};
12 changes: 12 additions & 0 deletions src/lib/commercial-features.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ class CommercialFeatures {
comscore: boolean;
launchpad: boolean;
youtubeAdvertising: boolean;
footballFixturesAdverts: boolean;

constructor() {
// this is used for SpeedCurve tests
Expand All @@ -83,6 +84,17 @@ class CommercialFeatures {
// TODO Convert detect.js to TypeScript
const isUnsupportedBrowser: boolean = isInternetExplorer();

// Detect presence of space for football-right ad slot
const { pageId } = window.guardian.config.page;
const isFootballPage = pageId.startsWith('football/');
const isPageWithRightAdSpace =
pageId.endsWith('/fixtures') ||
pageId.endsWith('/results') ||
pageId.endsWith('/tables') ||
pageId.endsWith('/table');

this.footballFixturesAdverts = isFootballPage && isPageWithRightAdSpace;

this.isSecureContact = [
'help/ng-interactive/2017/mar/17/contact-the-guardian-securely',
'help/2016/sep/19/how-to-contact-the-guardian-securely',
Expand Down
1 change: 1 addition & 0 deletions src/lib/header-bidding/prebid-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export type HeaderBiddingSlotName =
| 'comments-expanded'
| 'crossword-banner'
| 'crossword-banner-mobile'
| 'football-right'
| 'mobile-sticky'
| 'mostpop'
| 'right'
Expand Down
8 changes: 8 additions & 0 deletions src/lib/header-bidding/slot-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,14 @@ const getSlots = (): HeaderBiddingSizeMapping => {
'crossword-banner-mobile': {
mobile: [adSizes.mobilesticky],
},
'football-right': {
desktop: [
adSizes.empty,
adSizes.mpu,
adSizes.skyscraper,
adSizes.halfPage,
],
},
merchandising: {
mobile: [adSizes.mpu],
desktop: [adSizes.billboard],
Expand Down
14 changes: 13 additions & 1 deletion src/lib/header-bidding/utils.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ const resetConfig = () => {
window.guardian.config.page.section = 'Magic';
window.guardian.config.page.edition = 'UK';
window.guardian.config.page.isDev = false;
window.guardian.config.page.pageId = '';
};

describe('Utils', () => {
Expand Down Expand Up @@ -365,8 +366,19 @@ describe('Utils', () => {
},
);

test('shouldIncludeMobileSticky should be false if all conditions true except content type ', () => {
test.each(regionsTestCases)(
`should include mobile sticky $expected if geolocation is $region and pageId is football/ on mobiles`,
({ region, expected }) => {
window.guardian.config.page.pageId = 'football/';
getCountryCode.mockReturnValue(region);
matchesBreakpoints.mockReturnValue(true);
expect(shouldIncludeMobileSticky()).toBe(expected);
},
);

test('shouldIncludeMobileSticky should be false if all conditions true except pageId or content type ', () => {
window.guardian.config.page.contentType = 'Network Front';
window.guardian.config.page.pageId = 'lifeandstyle/';
matchesBreakpoints.mockReturnValue(true);
getCountryCode.mockReturnValue('US');
expect(shouldIncludeMobileSticky()).toBe(false);
Expand Down
7 changes: 6 additions & 1 deletion src/lib/header-bidding/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,11 @@ const contains = (
size: HeaderBiddingSize,
): boolean => Boolean(sizes.find((s) => s[0] === size[0] && s[1] === size[1]));

const isValidPageForMobileSticky = (): boolean => {
const { contentType, pageId } = window.guardian.config.page;
return contentType === 'Article' || pageId.startsWith('football/');
};

/**
* Cleans an object for targetting. Removes empty strings and other falsy values.
* @param o object with falsy values
Expand Down Expand Up @@ -197,7 +202,7 @@ export const shouldIncludeMobileSticky = once(
max: 'mobileLandscape',
}) &&
!isInUk() &&
window.guardian.config.page.contentType === 'Article' &&
isValidPageForMobileSticky() &&
!window.guardian.config.page.isHosted),
);

Expand Down

0 comments on commit e1b6032

Please sign in to comment.