first run at responsive ads

This commit is contained in:
Jon McClure 2024-03-15 09:54:12 +00:00
parent 85142fe932
commit e6436dd3ae
13 changed files with 273 additions and 204 deletions

View file

@ -55,6 +55,7 @@
"@storybook/theming": "^7.4.2",
"@sveltejs/vite-plugin-svelte": "^2.4.1",
"@tsconfig/svelte": "^4.0.1",
"@types/google-publisher-tag": "^1.20240219.0",
"@types/gtag.js": "^0.0.12",
"@types/mdx": "^2.0.5",
"@types/proper-url-join": "^2.1.1",

View file

@ -0,0 +1,31 @@
export type AdType =
| 'leaderboard'
| 'sponsorlogo'
| 'native'
| 'mpu'
| 'billboard';
export type PlacementName =
| 'reuters_desktop_leaderboard_atf'
| 'reuters_mobile_leaderboard'
| 'reuters_desktop_native_1'
| 'reuters_mobile_mpu_1'
| 'reuters_sponsorlogo'
| 'reuters_billboard_desktop';
export type DesktopPlacementName =
| 'reuters_desktop_leaderboard_atf'
| 'reuters_desktop_native_1'
| 'reuters_sponsorlogo'
| 'reuters_billboard_desktop';
export type MobilePlacementName<DesktopPlacementName extends string> =
DesktopPlacementName extends 'reuters_desktop_leaderboard_atf'
? 'reuters_mobile_leaderboard'
: DesktopPlacementName extends 'reuters_desktop_native_1'
? 'reuters_mobile_mpu_1'
: DesktopPlacementName extends 'reuters_sponsorlogo'
? 'reuters_sponsorlogo'
: DesktopPlacementName extends 'reuters_billboard_desktop'
? 'reuters_mobile_mpu_1'
: never;

View file

@ -2,35 +2,7 @@
import { onMount } from 'svelte';
import { loadBootstrap } from './adScripts/bootstrap';
import { loadScript } from './adScripts/loadScript';
import { throttle } from 'lodash-es';
let lastScroll = 0;
let showManagePreferences = true;
const togglePrefs = (on = true) => {
const btn = document.getElementById('ot-sdk-btn-floating');
if (!btn) return;
if (on) {
showManagePreferences = true;
btn.style.bottom = '';
} else {
showManagePreferences = false;
btn.style.bottom = '-5rem';
}
};
const handleScroll = () => {
if (lastScroll > window.scrollY) {
if (!showManagePreferences) {
togglePrefs(true);
}
} else {
if (showManagePreferences && window.scrollY > 250) {
togglePrefs(false);
}
}
lastScroll = window.scrollY;
};
import OneTrust from './OneTrust.svelte';
onMount(() => {
window.graphicsAdQueue = window.graphicsAdQueue || [];
@ -38,9 +10,6 @@
'https://graphics.thomsonreuters.com/cdn/js/bootstrap.static.js',
loadBootstrap
);
window.addEventListener('scroll', throttle(handleScroll, 250), {
passive: true,
});
});
</script>
@ -60,3 +29,5 @@
/>
<link rel="stylesheet" href="https://a.pub.network/reuters-com/cls.css" />
</svelte:head>
<OneTrust />

View file

@ -1,46 +0,0 @@
<script>
import { Meta, Template, Story } from '@storybook/addon-svelte-csf';
// Don't lose the "?raw" in markdown imports!
// @ts-ignore
import componentDocs from './stories/docs/component.md?raw';
import AdScripts from './AdScripts.svelte';
import AdSlot from './AdSlot.svelte';
import { withComponentDocs } from '$docs/utils/withParams.js';
const meta = {
title: 'Components/AdSlot',
component: AdSlot,
...withComponentDocs(componentDocs),
argTypes: {
placementName: {
control: 'select',
options: ['reuters_desktop_native_1', 'reuters_desktop_canvas'],
},
dataFreestarAd: {
control: 'select',
options: ['__970x250'],
},
},
};
</script>
<Meta {...meta} />
<Template let:args>
<div>
<AdScripts />
<AdSlot {...args} />
<AdSlot placementName="reuters_desktop_canvas" dataFreestarAd="__970x250" />
</div>
</Template>
<Story
name="Default"
args="{{
placementName: 'reuters_desktop_native_1',
dataFreestarAd: '__970x250',
}}"
/>

View file

@ -1,126 +1,29 @@
<script lang="ts">
/** ✏️ DOCUMENT your chart's props using TypeScript and JSDoc comments like below! */
import type { PlacementName, AdType } from './@types/ads';
import { onMount } from 'svelte';
import { getRandomAdId } from './utils';
type PlacementName =
// Disabling leaderboard for now...
// | 'reuters_desktop_leaderboard_atf'
// | 'reuters_mobile_leaderboard'
| 'reuters_desktop_native_1'
| 'reuters_mobile_mpu_1'
| 'reuters_sponsorlogo'
| 'reuters_desktop_canvas';
export let placementName: PlacementName;
export let adType: AdType;
/**
* The unique placement name from FreeStar dashboard.
* @required
*/
export let placementName: PlacementName = 'reuters_desktop_native_1';
/**
* The unique slot Id from FreeStar dashboard.
* @TODO Unclear at what level this bit of config is used with placements...
*/
export let dataFreestarAd: string = '__970x250';
/** Add an ID to target with SCSS. */
export let id: string = '';
/** Add a class to target with SCSS. */
let cls: string = 'my-12';
export { cls as class };
import { onMount } from 'svelte';
import Block from '../Block/Block.svelte';
const random4 = () =>
Math.floor((1 + Math.random()) * 0x10000)
.toString(16)
.substring(1);
const randomAdId = 'ad-' + random4() + random4();
const getAdType = (placementName: PlacementName) => {
switch (placementName) {
// case 'reuters_desktop_leaderboard_atf':
// case 'reuters_mobile_leaderboard':
// return 'leaderboard';
case 'reuters_sponsorlogo':
return 'Sponsorlogo';
case 'reuters_mobile_mpu_1':
return 'mpu';
default:
return 'native';
}
};
const adId = getRandomAdId();
onMount(() => {
const urlParams = new URLSearchParams(window.location.search);
const adstest = urlParams.get('adstest');
window.graphicsAdQueue = window.graphicsAdQueue || [];
window.graphicsAdQueue.push({
placementName,
slotId: randomAdId,
type: getAdType(placementName),
graphicId: window.location.pathname,
adstest,
slotId: adId,
targeting: {
div_id: adId,
type: adType,
},
});
});
</script>
<!-- @component `AdSlot` [Read the docs.](https://reuters-graphics.github.io/graphics-components/?path=/docs/components-AdSlot--default) -->
<Block id="{id}" class="freestar-adslot {cls}">
<div class="ad-block">
<div class="ad-label">Advertisement · Scroll to continue</div>
<div class="ad-container">
<div class="ad-slot__inner">
<div>
<div
data-freestar-ad="{dataFreestarAd || null}"
id="{randomAdId}"
></div>
</div>
</div>
</div>
</div>
</Block>
<style lang="scss">
div.ad-block {
border-bottom: 1px solid var(--theme-colour-brand-rules);
border-top: 1px solid var(--theme-colour-brand-rules);
div.ad-label {
font-family: Knowledge, 'Source Sans Pro', Arial, Helvetica, sans-serif;
font-size: 14px;
margin: 6px 0;
line-height: 1.333;
color: var(--theme-colour-text-secondary);
width: 100%;
text-align: center;
}
div.ad-container {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
min-height: 415px;
@media (max-width: 767.9px) {
min-height: 320px;
}
div.ad-slot__inner {
margin: auto 0;
width: 100%;
max-width: 100%;
flex: unset;
& > div {
display: block;
div[data-freestar-ad] {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
}
}
}
}
</style>
<div data-freestar-ad="{dataFreestarAd || null}" id="{adId}"></div>

View file

@ -0,0 +1,29 @@
<script>
import { Meta, Template, Story } from '@storybook/addon-svelte-csf';
// @ts-ignore
import componentDocs from './stories/docs/component.md?raw';
import AdScripts from './AdScripts.svelte';
import InlineAd from './InlineAd.svelte';
import { withComponentDocs } from '$docs/utils/withParams.js';
const meta = {
title: 'Components/InlineAd',
component: InlineAd,
...withComponentDocs(componentDocs),
};
</script>
<Meta title="Components/InlineAd" {...meta} />
<Template let:args>
<div>
<AdScripts />
<InlineAd />
<InlineAd />
</div>
</Template>
<Story name="Default" />

View file

@ -0,0 +1,69 @@
<script lang="ts">
import Block from '../Block/Block.svelte';
import { DesktopPlacementName } from './@types/ads';
import ResponsiveAd from './ResponsiveAd.svelte';
/** Add an ID to target with SCSS. */
export let id: string = '';
/** Add a class to target with SCSS. */
let cls: string = 'my-12';
export { cls as class };
const desktopPlacementName: DesktopPlacementName = 'reuters_desktop_native_1';
</script>
<!-- @component `InlineAd` [Read the docs.](https://reuters-graphics.github.io/graphics-components/?path=/docs/components-InlineAd--default) -->
<Block id="{id}" class="freestar-adslot {cls}">
<div class="ad-block">
<div class="ad-label">Advertisement · Scroll to continue</div>
<div class="ad-container">
<div class="ad-slot__inner">
<div>
<ResponsiveAd desktopPlacementName="{desktopPlacementName}" />
</div>
</div>
</div>
</div>
</Block>
<style lang="scss">
div.ad-block {
border-bottom: 1px solid var(--theme-colour-brand-rules);
border-top: 1px solid var(--theme-colour-brand-rules);
div.ad-label {
font-family: Knowledge, 'Source Sans Pro', Arial, Helvetica, sans-serif;
font-size: 14px;
margin: 6px 0;
line-height: 1.333;
color: var(--theme-colour-text-secondary);
width: 100%;
text-align: center;
}
div.ad-container {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
min-height: 415px;
@media (max-width: 767.9px) {
min-height: 320px;
}
div.ad-slot__inner {
margin: auto 0;
width: 100%;
max-width: 100%;
flex: unset;
& > div {
display: block;
:global(div[data-freestar-ad]) {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
}
}
}
}
</style>

View file

@ -0,0 +1,39 @@
<!-- This component manages the OneTrust prefs button, so it's not permanently fixed on page... -->
<script>
import { onMount } from 'svelte';
import { throttle } from 'lodash-es';
let lastScroll = 0;
let showManagePreferences = true;
const togglePrefs = (on = true) => {
const btn = document.getElementById('ot-sdk-btn-floating');
if (!btn) return;
if (on) {
showManagePreferences = true;
btn.style.bottom = '';
} else {
showManagePreferences = false;
btn.style.bottom = '-5rem';
}
};
const handleScroll = () => {
if (lastScroll > window.scrollY) {
if (!showManagePreferences) {
togglePrefs(true);
}
} else {
if (showManagePreferences && window.scrollY > 250) {
togglePrefs(false);
}
}
lastScroll = window.scrollY;
};
onMount(() => {
window.addEventListener('scroll', throttle(handleScroll, 250), {
passive: true,
});
});
</script>

View file

@ -0,0 +1,52 @@
<script lang="ts">
import type { DesktopPlacementName, PlacementName } from './@types/ads';
import AdSlot from './AdSlot.svelte';
export let desktopPlacementName: DesktopPlacementName;
export let mobileBreakpoint = 1024;
let windowWidth: number;
const getMobilePlacementName = (
desktopPlacementName: DesktopPlacementName
) => {
switch (desktopPlacementName) {
case 'reuters_desktop_leaderboard_atf':
return 'reuters_mobile_leaderboard' as const;
case 'reuters_sponsorlogo':
return 'reuters_sponsorlogo' as const;
default:
return 'reuters_mobile_mpu_1' as const;
}
};
const getAdType = (placementName: PlacementName) => {
switch (placementName) {
case 'reuters_desktop_leaderboard_atf':
case 'reuters_mobile_leaderboard':
return 'leaderboard' as const;
case 'reuters_sponsorlogo':
return 'sponsorlogo' as const;
case 'reuters_mobile_mpu_1':
return 'mpu' as const;
case 'reuters_billboard_desktop':
return 'billboard' as const;
default:
return 'native' as const;
}
};
$: placementName =
windowWidth && windowWidth < mobileBreakpoint
? getMobilePlacementName(desktopPlacementName)
: desktopPlacementName;
$: adType = getAdType(placementName);
</script>
<svelte:window bind:innerWidth="{windowWidth}" />
{#if windowWidth}
{#key placementName}
<AdSlot placementName="{placementName}" adType="{adType}" />
{/key}
{/if}

View file

@ -46,19 +46,15 @@ export const loadBootstrap = () => {
loadScript('https://a.pub.network/reuters-com/pubfig.min.js');
// Set GAM
(<any>window).googletag = (<any>window).googletag || { cmd: [] };
(<any>window).googletag.cmd.push(() => {
(<any>window).googletag.pubads().enableSingleRequest();
(<any>window).googletag.pubads().enableAsyncRendering();
(<any>window).googletag.pubads().collapseEmptyDivs(true);
// Global Ads test targeting
const adstest = new URL(document.location.href).searchParams.get('adstest');
if (adstest) {
(<any>window).googletag.pubads().setTargeting('adstest', adstest);
}
(<any>window).googletag.pubads().setTargeting('template', 'graphics');
window.googletag = (<any>window).googletag || { cmd: [] };
window.googletag.cmd.push(() => {
window.googletag.pubads().enableSingleRequest();
/**
* @TODO Property 'enableAsyncRendering' does not exist on type 'PubAdsService'.
*/
// @ts-ignore
window.googletag.pubads().enableAsyncRendering();
window.googletag.pubads().collapseEmptyDivs(true);
});
if (!Array.isArray((<any>window).graphicsAdQueue)) {
@ -66,11 +62,24 @@ export const loadBootstrap = () => {
}
freestar.queue.push(function() {
freestar.newAdSlots((<any>window).graphicsAdQueue || [], 'foobar');
freestar.newAdSlots((<any>window).graphicsAdQueue || [], freestar.config.channel);
});
// Set page-level key-values
// cf: https://help.freestar.com/help/using-key-values
freestar.queue.push(function() {
(<any>window).googletag.pubads().set('page_url', 'https://www.reuters.com/'); // This line should only be used for testing
// Global Ads test targeting
const adstest = new URL(document.location.href).searchParams.get('adstest');
if (adstest) {
window.googletag.pubads().setTargeting('adstest', adstest);
}
// Use the URL path to create a unique ID for the page.
const graphicId = window.location.pathname
.replace(/^\/(.*)\/$/, '$1')
.replaceAll('/', '-');
window.googletag.pubads().setTargeting('template', 'graphics');
window.googletag.pubads().setTargeting('graphicId', graphicId);
});
});
};

View file

@ -0,0 +1,6 @@
const random4 = () =>
Math.floor((1 + Math.random()) * 0x10000)
.toString(16)
.substring(1);
export const getRandomAdId = () => 'ad-' + random4() + random4();

View file

@ -9,7 +9,7 @@ export {
} from './components/Analytics/Analytics.svelte';
export { default as Article } from './components/Article/Article.svelte';
export { default as AdScripts } from './components/AdSlot/AdScripts.svelte';
export { default as AdSlot } from './components/AdSlot/AdSlot.svelte';
export { default as InlineAd } from './components/AdSlot/InlineAd.svelte';
export { default as BeforeAfter } from './components/BeforeAfter/BeforeAfter.svelte';
export { default as Block } from './components/Block/Block.svelte';
export { default as BodyText } from './components/BodyText/BodyText.svelte';

View file

@ -2993,6 +2993,11 @@
resolved "https://registry.npmjs.org/@types/find-cache-dir/-/find-cache-dir-3.2.1.tgz"
integrity sha512-frsJrz2t/CeGifcu/6uRo4b+SzAwT4NYCVPu1GN8IB9XTzrpPkGuV0tmh9mN+/L0PklAlsC3u5Fxt0ju00LXIw==
"@types/google-publisher-tag@^1.20240219.0":
version "1.20240219.0"
resolved "https://registry.yarnpkg.com/@types/google-publisher-tag/-/google-publisher-tag-1.20240219.0.tgz#4b883f033b923bf161f61942adb58c58a7dbea16"
integrity sha512-CvXeyjN9deEo6+nAqgHsGItPhWKnqkeLOqGlZmrb7inO8KUUQTmk2xfVqUnwIi6YKfBrpV4ZODd1TkgnXaIAjw==
"@types/graceful-fs@^4.1.3":
version "4.1.6"
resolved "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.6.tgz"