@guardian/commercial-core 0.23.0 → 0.26.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/ad-targeting-youtube.d.ts +6 -0
- package/dist/cjs/ad-targeting-youtube.js +65 -0
- package/dist/cjs/constants/index.d.ts +2 -0
- package/dist/cjs/constants/index.js +7 -0
- package/dist/cjs/constants/prebidTimeout.d.ts +6 -0
- package/dist/cjs/constants/prebidTimeout.js +14 -0
- package/dist/cjs/constants/topAboveNavHeight.d.ts +10 -0
- package/dist/cjs/constants/topAboveNavHeight.js +48 -0
- package/dist/cjs/index.d.ts +2 -0
- package/dist/cjs/index.js +4 -1
- package/dist/cjs/lib/can-use-dom.d.ts +2 -0
- package/dist/cjs/lib/can-use-dom.js +9 -0
- package/dist/cjs/lib/construct-query.d.ts +3 -0
- package/dist/cjs/lib/construct-query.js +12 -0
- package/dist/cjs/permutive.js +8 -2
- package/dist/cjs/types.d.ts +25 -0
- package/dist/esm/ad-targeting-youtube.d.ts +6 -0
- package/dist/esm/ad-targeting-youtube.js +61 -0
- package/dist/esm/constants/index.d.ts +2 -0
- package/dist/esm/constants/index.js +2 -0
- package/dist/esm/constants/prebidTimeout.d.ts +6 -0
- package/dist/esm/constants/prebidTimeout.js +11 -0
- package/dist/esm/constants/topAboveNavHeight.d.ts +10 -0
- package/dist/esm/constants/topAboveNavHeight.js +45 -0
- package/dist/esm/index.d.ts +2 -0
- package/dist/esm/index.js +1 -0
- package/dist/esm/lib/can-use-dom.d.ts +2 -0
- package/dist/esm/lib/can-use-dom.js +6 -0
- package/dist/esm/lib/construct-query.d.ts +3 -0
- package/dist/esm/lib/construct-query.js +9 -0
- package/dist/esm/permutive.js +8 -2
- package/dist/esm/types.d.ts +25 -0
- package/package.json +3 -2
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { ConsentState } from '@guardian/consent-management-platform/dist/types';
|
|
2
|
+
import type { AdsConfig, AdsConfigDisabled, MaybeArray } from './types';
|
|
3
|
+
declare type CustomParams = Record<string, MaybeArray<string | number | boolean>>;
|
|
4
|
+
declare const disabledAds: AdsConfigDisabled;
|
|
5
|
+
declare const buildAdsConfigWithConsent: (isAdFreeUser: boolean, adUnit: string, customParamsToMerge: CustomParams, consentState: ConsentState) => AdsConfig;
|
|
6
|
+
export { buildAdsConfigWithConsent, disabledAds };
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.disabledAds = exports.buildAdsConfigWithConsent = void 0;
|
|
4
|
+
const libs_1 = require("@guardian/libs");
|
|
5
|
+
const can_use_dom_1 = require("./lib/can-use-dom");
|
|
6
|
+
const construct_query_1 = require("./lib/construct-query");
|
|
7
|
+
const permutive_1 = require("./permutive");
|
|
8
|
+
const buildCustomParamsFromCookies = () => (0, can_use_dom_1.canUseDom)()
|
|
9
|
+
? {
|
|
10
|
+
permutive: (0, permutive_1.getPermutivePFPSegments)(),
|
|
11
|
+
si: (0, libs_1.getCookie)({ name: 'GU_U' }) ? 't' : 'f',
|
|
12
|
+
}
|
|
13
|
+
: {};
|
|
14
|
+
const buildAdsConfig = (cmpConsent, adUnit, customParams) => {
|
|
15
|
+
const mergedCustomParams = {
|
|
16
|
+
...customParams,
|
|
17
|
+
...buildCustomParamsFromCookies(),
|
|
18
|
+
};
|
|
19
|
+
const defaultAdsConfig = {
|
|
20
|
+
adTagParameters: {
|
|
21
|
+
iu: adUnit,
|
|
22
|
+
// TODO: Why are we double encoding? Following Frontend process for now
|
|
23
|
+
cust_params: encodeURIComponent((0, construct_query_1.constructQuery)(mergedCustomParams)),
|
|
24
|
+
},
|
|
25
|
+
};
|
|
26
|
+
if (cmpConsent.ccpa) {
|
|
27
|
+
const canTarget = !cmpConsent.ccpa.doNotSell;
|
|
28
|
+
return {
|
|
29
|
+
...defaultAdsConfig,
|
|
30
|
+
restrictedDataProcessor: !canTarget,
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
if (cmpConsent.aus) {
|
|
34
|
+
const canTarget = cmpConsent.aus.personalisedAdvertising;
|
|
35
|
+
return {
|
|
36
|
+
...defaultAdsConfig,
|
|
37
|
+
restrictedDataProcessor: !canTarget,
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
if (cmpConsent.tcfv2) {
|
|
41
|
+
const tcfData = cmpConsent.tcfv2;
|
|
42
|
+
const canTarget = Object.values(tcfData.consents).every(Boolean);
|
|
43
|
+
const mergedAdTagParameters = {
|
|
44
|
+
...defaultAdsConfig.adTagParameters,
|
|
45
|
+
cmpGdpr: tcfData.gdprApplies ? 1 : 0,
|
|
46
|
+
cmpGvcd: tcfData.addtlConsent,
|
|
47
|
+
cmpVcd: tcfData.tcString,
|
|
48
|
+
};
|
|
49
|
+
return {
|
|
50
|
+
adTagParameters: mergedAdTagParameters,
|
|
51
|
+
nonPersonalizedAd: !canTarget,
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
// Shouldn't happen but handle if no matching framework
|
|
55
|
+
return disabledAds;
|
|
56
|
+
};
|
|
57
|
+
const disabledAds = { disableAds: true };
|
|
58
|
+
exports.disabledAds = disabledAds;
|
|
59
|
+
const buildAdsConfigWithConsent = (isAdFreeUser, adUnit, customParamsToMerge, consentState) => {
|
|
60
|
+
if (isAdFreeUser) {
|
|
61
|
+
return disabledAds;
|
|
62
|
+
}
|
|
63
|
+
return buildAdsConfig(consentState, adUnit, customParamsToMerge);
|
|
64
|
+
};
|
|
65
|
+
exports.buildAdsConfigWithConsent = buildAdsConfigWithConsent;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.TOP_ABOVE_NAV_HEIGHT = exports.PREBID_TIMEOUT = void 0;
|
|
4
|
+
var prebidTimeout_1 = require("./prebidTimeout");
|
|
5
|
+
Object.defineProperty(exports, "PREBID_TIMEOUT", { enumerable: true, get: function () { return prebidTimeout_1.PREBID_TIMEOUT; } });
|
|
6
|
+
var topAboveNavHeight_1 = require("./topAboveNavHeight");
|
|
7
|
+
Object.defineProperty(exports, "TOP_ABOVE_NAV_HEIGHT", { enumerable: true, get: function () { return topAboveNavHeight_1.TOP_ABOVE_NAV_HEIGHT; } });
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PREBID_TIMEOUT = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Unit: milliseconds.
|
|
6
|
+
*
|
|
7
|
+
* This value is currently being investigated.
|
|
8
|
+
*/
|
|
9
|
+
exports.PREBID_TIMEOUT = 1500;
|
|
10
|
+
/*
|
|
11
|
+
|
|
12
|
+
cc @chrislomaxjones
|
|
13
|
+
|
|
14
|
+
*/
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unit: pixels.
|
|
3
|
+
*
|
|
4
|
+
* The majority of ads in the top banner are 250px high. We ran an experiment
|
|
5
|
+
* in October 2021 to set the minimum height to 250, and let smaller ads be
|
|
6
|
+
* centred in the space. We did not process with this option, as it had a
|
|
7
|
+
* negative impact on viewability and revenue.
|
|
8
|
+
*
|
|
9
|
+
*/
|
|
10
|
+
export declare const TOP_ABOVE_NAV_HEIGHT = 90;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.TOP_ABOVE_NAV_HEIGHT = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Unit: pixels.
|
|
6
|
+
*
|
|
7
|
+
* The majority of ads in the top banner are 250px high. We ran an experiment
|
|
8
|
+
* in October 2021 to set the minimum height to 250, and let smaller ads be
|
|
9
|
+
* centred in the space. We did not process with this option, as it had a
|
|
10
|
+
* negative impact on viewability and revenue.
|
|
11
|
+
*
|
|
12
|
+
*/
|
|
13
|
+
exports.TOP_ABOVE_NAV_HEIGHT = 90;
|
|
14
|
+
/*
|
|
15
|
+
Further Notes
|
|
16
|
+
=============
|
|
17
|
+
|
|
18
|
+
There was a very positive impact on CLS (Cumulative Layout Shift), which is good
|
|
19
|
+
for UX. However, the negative commercial impact meant we kept a height of 90px.
|
|
20
|
+
|
|
21
|
+
We ran a 1% server-side experiment to measure CLS when dedicating 250px for the
|
|
22
|
+
topAboveNav. The experiment showed this change has a significant positive impact
|
|
23
|
+
on CLS, and moves average CLS for the page from 0.09 to 0.07 (a 26% improvement
|
|
24
|
+
from this one change). The other way we sliced the data was to look at the
|
|
25
|
+
percent of pages that Google categorised as having 'good', 'needs improvement'
|
|
26
|
+
or 'poor' CLS scores. The viewability for the page dropped by about 1%, and for
|
|
27
|
+
that specific slot, by 4-6%.
|
|
28
|
+
|
|
29
|
+
When the experiment ran, the breakdown was as follows:
|
|
30
|
+
|
|
31
|
+
- 74% of our pages have a “good” CLS score
|
|
32
|
+
- 12% have a “poor” CLS score.
|
|
33
|
+
- 70% viewability for top-above-nav
|
|
34
|
+
|
|
35
|
+
This change resulted in:
|
|
36
|
+
|
|
37
|
+
- 84% “good”
|
|
38
|
+
- 4% “poor”
|
|
39
|
+
- 64% viewability for top-above-nav
|
|
40
|
+
|
|
41
|
+
Relevant Pull Requests
|
|
42
|
+
----------------------
|
|
43
|
+
|
|
44
|
+
- https://github.com/guardian/frontend/pull/24095
|
|
45
|
+
- https://github.com/guardian/dotcom-rendering/pull/3497
|
|
46
|
+
- https://github.com/guardian/dotcom-rendering/pull/3340
|
|
47
|
+
|
|
48
|
+
*/
|
package/dist/cjs/index.d.ts
CHANGED
|
@@ -11,3 +11,5 @@ export { adSizes } from './ad-sizes';
|
|
|
11
11
|
export type { SizeKeys, AdSizeString, AdSize } from './ad-sizes';
|
|
12
12
|
export { isAdBlockInUse } from './detectAdBlocker';
|
|
13
13
|
export { clearPermutiveSegments, getPermutiveSegments, getPermutivePFPSegments, } from './permutive';
|
|
14
|
+
export { buildAdsConfigWithConsent, disabledAds } from './ad-targeting-youtube';
|
|
15
|
+
export type { AdsConfig, AdsConfigBasic, AdsConfigDisabled, AdTargetingBuilder, CustomParams, } from './types';
|
package/dist/cjs/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
/* istanbul ignore file -- there's no point check this for test coverage */
|
|
3
3
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
-
exports.getPermutivePFPSegments = exports.getPermutiveSegments = exports.clearPermutiveSegments = exports.isAdBlockInUse = exports.adSizes = exports.sendCommercialMetrics = exports.EventTimer = exports.remarketing = exports.inizio = exports.twitter = exports.fbPixel = exports.permutive = exports.ias = void 0;
|
|
4
|
+
exports.disabledAds = exports.buildAdsConfigWithConsent = exports.getPermutivePFPSegments = exports.getPermutiveSegments = exports.clearPermutiveSegments = exports.isAdBlockInUse = exports.adSizes = exports.sendCommercialMetrics = exports.EventTimer = exports.remarketing = exports.inizio = exports.twitter = exports.fbPixel = exports.permutive = exports.ias = void 0;
|
|
5
5
|
var ias_1 = require("./third-party-tags/ias");
|
|
6
6
|
Object.defineProperty(exports, "ias", { enumerable: true, get: function () { return ias_1.ias; } });
|
|
7
7
|
var permutive_1 = require("./third-party-tags/permutive");
|
|
@@ -26,3 +26,6 @@ var permutive_2 = require("./permutive");
|
|
|
26
26
|
Object.defineProperty(exports, "clearPermutiveSegments", { enumerable: true, get: function () { return permutive_2.clearPermutiveSegments; } });
|
|
27
27
|
Object.defineProperty(exports, "getPermutiveSegments", { enumerable: true, get: function () { return permutive_2.getPermutiveSegments; } });
|
|
28
28
|
Object.defineProperty(exports, "getPermutivePFPSegments", { enumerable: true, get: function () { return permutive_2.getPermutivePFPSegments; } });
|
|
29
|
+
var ad_targeting_youtube_1 = require("./ad-targeting-youtube");
|
|
30
|
+
Object.defineProperty(exports, "buildAdsConfigWithConsent", { enumerable: true, get: function () { return ad_targeting_youtube_1.buildAdsConfigWithConsent; } });
|
|
31
|
+
Object.defineProperty(exports, "disabledAds", { enumerable: true, get: function () { return ad_targeting_youtube_1.disabledAds; } });
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Based on https://github.com/JedWatson/exenv
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.canUseDom = void 0;
|
|
5
|
+
const canUseDom = () => !!(typeof window !== 'undefined' &&
|
|
6
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- ensure we check at runtime
|
|
7
|
+
window.document &&
|
|
8
|
+
window.document.createElement);
|
|
9
|
+
exports.canUseDom = canUseDom;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.constructQuery = void 0;
|
|
4
|
+
const constructQuery = (query) => Object.entries(query)
|
|
5
|
+
.map(([key, value]) => {
|
|
6
|
+
const queryValue = Array.isArray(value)
|
|
7
|
+
? value.map((v) => encodeURIComponent(v)).join(',')
|
|
8
|
+
: encodeURIComponent(value);
|
|
9
|
+
return `${key}=${queryValue}`;
|
|
10
|
+
})
|
|
11
|
+
.join('&');
|
|
12
|
+
exports.constructQuery = constructQuery;
|
package/dist/cjs/permutive.js
CHANGED
|
@@ -6,13 +6,19 @@ const PERMUTIVE_KEY = `_papns`;
|
|
|
6
6
|
const PERMUTIVE_PFP_KEY = `_pdfps`;
|
|
7
7
|
const getSegments = (key) => {
|
|
8
8
|
try {
|
|
9
|
-
|
|
9
|
+
const rawSegments = libs_1.storage.local.getRaw(key);
|
|
10
|
+
const segments = rawSegments
|
|
11
|
+
? JSON.parse(rawSegments)
|
|
12
|
+
: null;
|
|
13
|
+
if (!Array.isArray(segments))
|
|
14
|
+
return [];
|
|
15
|
+
return segments
|
|
10
16
|
.slice(0, 250)
|
|
11
17
|
.map((s) => Number.parseInt(s, 10))
|
|
12
18
|
.filter((n) => typeof n === 'number' && !Number.isNaN(n))
|
|
13
19
|
.map(String);
|
|
14
20
|
}
|
|
15
|
-
catch (
|
|
21
|
+
catch (err) {
|
|
16
22
|
return [];
|
|
17
23
|
}
|
|
18
24
|
};
|
package/dist/cjs/types.d.ts
CHANGED
|
@@ -29,3 +29,28 @@ export declare type GoogleTrackConversionObject = {
|
|
|
29
29
|
google_custom_params: GoogleTagParams;
|
|
30
30
|
google_remarketing_only: boolean;
|
|
31
31
|
};
|
|
32
|
+
export declare type MaybeArray<T> = T | T[];
|
|
33
|
+
export declare type CustomParams = Record<string, MaybeArray<string | number | boolean>>;
|
|
34
|
+
export declare type AdsConfigDisabled = {
|
|
35
|
+
disableAds: true;
|
|
36
|
+
};
|
|
37
|
+
export declare type AdsConfigBasic = {
|
|
38
|
+
adTagParameters: {
|
|
39
|
+
iu: string;
|
|
40
|
+
cust_params: string;
|
|
41
|
+
};
|
|
42
|
+
};
|
|
43
|
+
export declare type AdsConfigCCPAorAus = AdsConfigBasic & {
|
|
44
|
+
restrictedDataProcessor: boolean;
|
|
45
|
+
};
|
|
46
|
+
export declare type AdsConfigTCFV2 = AdsConfigBasic & {
|
|
47
|
+
adTagParameters: {
|
|
48
|
+
cmpGdpr: number;
|
|
49
|
+
cmpVcd: string;
|
|
50
|
+
cmpGvcd: string;
|
|
51
|
+
};
|
|
52
|
+
nonPersonalizedAd: boolean;
|
|
53
|
+
};
|
|
54
|
+
export declare type AdsConfigEnabled = AdsConfigBasic | AdsConfigCCPAorAus | AdsConfigTCFV2;
|
|
55
|
+
export declare type AdsConfig = AdsConfigEnabled | AdsConfigDisabled;
|
|
56
|
+
export declare type AdTargetingBuilder = () => Promise<AdsConfig>;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { ConsentState } from '@guardian/consent-management-platform/dist/types';
|
|
2
|
+
import type { AdsConfig, AdsConfigDisabled, MaybeArray } from './types';
|
|
3
|
+
declare type CustomParams = Record<string, MaybeArray<string | number | boolean>>;
|
|
4
|
+
declare const disabledAds: AdsConfigDisabled;
|
|
5
|
+
declare const buildAdsConfigWithConsent: (isAdFreeUser: boolean, adUnit: string, customParamsToMerge: CustomParams, consentState: ConsentState) => AdsConfig;
|
|
6
|
+
export { buildAdsConfigWithConsent, disabledAds };
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { getCookie } from '@guardian/libs';
|
|
2
|
+
import { canUseDom } from './lib/can-use-dom';
|
|
3
|
+
import { constructQuery } from './lib/construct-query';
|
|
4
|
+
import { getPermutivePFPSegments } from './permutive';
|
|
5
|
+
const buildCustomParamsFromCookies = () => canUseDom()
|
|
6
|
+
? {
|
|
7
|
+
permutive: getPermutivePFPSegments(),
|
|
8
|
+
si: getCookie({ name: 'GU_U' }) ? 't' : 'f',
|
|
9
|
+
}
|
|
10
|
+
: {};
|
|
11
|
+
const buildAdsConfig = (cmpConsent, adUnit, customParams) => {
|
|
12
|
+
const mergedCustomParams = {
|
|
13
|
+
...customParams,
|
|
14
|
+
...buildCustomParamsFromCookies(),
|
|
15
|
+
};
|
|
16
|
+
const defaultAdsConfig = {
|
|
17
|
+
adTagParameters: {
|
|
18
|
+
iu: adUnit,
|
|
19
|
+
// TODO: Why are we double encoding? Following Frontend process for now
|
|
20
|
+
cust_params: encodeURIComponent(constructQuery(mergedCustomParams)),
|
|
21
|
+
},
|
|
22
|
+
};
|
|
23
|
+
if (cmpConsent.ccpa) {
|
|
24
|
+
const canTarget = !cmpConsent.ccpa.doNotSell;
|
|
25
|
+
return {
|
|
26
|
+
...defaultAdsConfig,
|
|
27
|
+
restrictedDataProcessor: !canTarget,
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
if (cmpConsent.aus) {
|
|
31
|
+
const canTarget = cmpConsent.aus.personalisedAdvertising;
|
|
32
|
+
return {
|
|
33
|
+
...defaultAdsConfig,
|
|
34
|
+
restrictedDataProcessor: !canTarget,
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
if (cmpConsent.tcfv2) {
|
|
38
|
+
const tcfData = cmpConsent.tcfv2;
|
|
39
|
+
const canTarget = Object.values(tcfData.consents).every(Boolean);
|
|
40
|
+
const mergedAdTagParameters = {
|
|
41
|
+
...defaultAdsConfig.adTagParameters,
|
|
42
|
+
cmpGdpr: tcfData.gdprApplies ? 1 : 0,
|
|
43
|
+
cmpGvcd: tcfData.addtlConsent,
|
|
44
|
+
cmpVcd: tcfData.tcString,
|
|
45
|
+
};
|
|
46
|
+
return {
|
|
47
|
+
adTagParameters: mergedAdTagParameters,
|
|
48
|
+
nonPersonalizedAd: !canTarget,
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
// Shouldn't happen but handle if no matching framework
|
|
52
|
+
return disabledAds;
|
|
53
|
+
};
|
|
54
|
+
const disabledAds = { disableAds: true };
|
|
55
|
+
const buildAdsConfigWithConsent = (isAdFreeUser, adUnit, customParamsToMerge, consentState) => {
|
|
56
|
+
if (isAdFreeUser) {
|
|
57
|
+
return disabledAds;
|
|
58
|
+
}
|
|
59
|
+
return buildAdsConfig(consentState, adUnit, customParamsToMerge);
|
|
60
|
+
};
|
|
61
|
+
export { buildAdsConfigWithConsent, disabledAds };
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unit: pixels.
|
|
3
|
+
*
|
|
4
|
+
* The majority of ads in the top banner are 250px high. We ran an experiment
|
|
5
|
+
* in October 2021 to set the minimum height to 250, and let smaller ads be
|
|
6
|
+
* centred in the space. We did not process with this option, as it had a
|
|
7
|
+
* negative impact on viewability and revenue.
|
|
8
|
+
*
|
|
9
|
+
*/
|
|
10
|
+
export declare const TOP_ABOVE_NAV_HEIGHT = 90;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unit: pixels.
|
|
3
|
+
*
|
|
4
|
+
* The majority of ads in the top banner are 250px high. We ran an experiment
|
|
5
|
+
* in October 2021 to set the minimum height to 250, and let smaller ads be
|
|
6
|
+
* centred in the space. We did not process with this option, as it had a
|
|
7
|
+
* negative impact on viewability and revenue.
|
|
8
|
+
*
|
|
9
|
+
*/
|
|
10
|
+
export const TOP_ABOVE_NAV_HEIGHT = 90;
|
|
11
|
+
/*
|
|
12
|
+
Further Notes
|
|
13
|
+
=============
|
|
14
|
+
|
|
15
|
+
There was a very positive impact on CLS (Cumulative Layout Shift), which is good
|
|
16
|
+
for UX. However, the negative commercial impact meant we kept a height of 90px.
|
|
17
|
+
|
|
18
|
+
We ran a 1% server-side experiment to measure CLS when dedicating 250px for the
|
|
19
|
+
topAboveNav. The experiment showed this change has a significant positive impact
|
|
20
|
+
on CLS, and moves average CLS for the page from 0.09 to 0.07 (a 26% improvement
|
|
21
|
+
from this one change). The other way we sliced the data was to look at the
|
|
22
|
+
percent of pages that Google categorised as having 'good', 'needs improvement'
|
|
23
|
+
or 'poor' CLS scores. The viewability for the page dropped by about 1%, and for
|
|
24
|
+
that specific slot, by 4-6%.
|
|
25
|
+
|
|
26
|
+
When the experiment ran, the breakdown was as follows:
|
|
27
|
+
|
|
28
|
+
- 74% of our pages have a “good” CLS score
|
|
29
|
+
- 12% have a “poor” CLS score.
|
|
30
|
+
- 70% viewability for top-above-nav
|
|
31
|
+
|
|
32
|
+
This change resulted in:
|
|
33
|
+
|
|
34
|
+
- 84% “good”
|
|
35
|
+
- 4% “poor”
|
|
36
|
+
- 64% viewability for top-above-nav
|
|
37
|
+
|
|
38
|
+
Relevant Pull Requests
|
|
39
|
+
----------------------
|
|
40
|
+
|
|
41
|
+
- https://github.com/guardian/frontend/pull/24095
|
|
42
|
+
- https://github.com/guardian/dotcom-rendering/pull/3497
|
|
43
|
+
- https://github.com/guardian/dotcom-rendering/pull/3340
|
|
44
|
+
|
|
45
|
+
*/
|
package/dist/esm/index.d.ts
CHANGED
|
@@ -11,3 +11,5 @@ export { adSizes } from './ad-sizes';
|
|
|
11
11
|
export type { SizeKeys, AdSizeString, AdSize } from './ad-sizes';
|
|
12
12
|
export { isAdBlockInUse } from './detectAdBlocker';
|
|
13
13
|
export { clearPermutiveSegments, getPermutiveSegments, getPermutivePFPSegments, } from './permutive';
|
|
14
|
+
export { buildAdsConfigWithConsent, disabledAds } from './ad-targeting-youtube';
|
|
15
|
+
export type { AdsConfig, AdsConfigBasic, AdsConfigDisabled, AdTargetingBuilder, CustomParams, } from './types';
|
package/dist/esm/index.js
CHANGED
|
@@ -10,3 +10,4 @@ export { sendCommercialMetrics } from './sendCommercialMetrics';
|
|
|
10
10
|
export { adSizes } from './ad-sizes';
|
|
11
11
|
export { isAdBlockInUse } from './detectAdBlocker';
|
|
12
12
|
export { clearPermutiveSegments, getPermutiveSegments, getPermutivePFPSegments, } from './permutive';
|
|
13
|
+
export { buildAdsConfigWithConsent, disabledAds } from './ad-targeting-youtube';
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
// Based on https://github.com/JedWatson/exenv
|
|
2
|
+
const canUseDom = () => !!(typeof window !== 'undefined' &&
|
|
3
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- ensure we check at runtime
|
|
4
|
+
window.document &&
|
|
5
|
+
window.document.createElement);
|
|
6
|
+
export { canUseDom };
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
const constructQuery = (query) => Object.entries(query)
|
|
2
|
+
.map(([key, value]) => {
|
|
3
|
+
const queryValue = Array.isArray(value)
|
|
4
|
+
? value.map((v) => encodeURIComponent(v)).join(',')
|
|
5
|
+
: encodeURIComponent(value);
|
|
6
|
+
return `${key}=${queryValue}`;
|
|
7
|
+
})
|
|
8
|
+
.join('&');
|
|
9
|
+
export { constructQuery };
|
package/dist/esm/permutive.js
CHANGED
|
@@ -3,13 +3,19 @@ const PERMUTIVE_KEY = `_papns`;
|
|
|
3
3
|
const PERMUTIVE_PFP_KEY = `_pdfps`;
|
|
4
4
|
const getSegments = (key) => {
|
|
5
5
|
try {
|
|
6
|
-
|
|
6
|
+
const rawSegments = storage.local.getRaw(key);
|
|
7
|
+
const segments = rawSegments
|
|
8
|
+
? JSON.parse(rawSegments)
|
|
9
|
+
: null;
|
|
10
|
+
if (!Array.isArray(segments))
|
|
11
|
+
return [];
|
|
12
|
+
return segments
|
|
7
13
|
.slice(0, 250)
|
|
8
14
|
.map((s) => Number.parseInt(s, 10))
|
|
9
15
|
.filter((n) => typeof n === 'number' && !Number.isNaN(n))
|
|
10
16
|
.map(String);
|
|
11
17
|
}
|
|
12
|
-
catch (
|
|
18
|
+
catch (err) {
|
|
13
19
|
return [];
|
|
14
20
|
}
|
|
15
21
|
};
|
package/dist/esm/types.d.ts
CHANGED
|
@@ -29,3 +29,28 @@ export declare type GoogleTrackConversionObject = {
|
|
|
29
29
|
google_custom_params: GoogleTagParams;
|
|
30
30
|
google_remarketing_only: boolean;
|
|
31
31
|
};
|
|
32
|
+
export declare type MaybeArray<T> = T | T[];
|
|
33
|
+
export declare type CustomParams = Record<string, MaybeArray<string | number | boolean>>;
|
|
34
|
+
export declare type AdsConfigDisabled = {
|
|
35
|
+
disableAds: true;
|
|
36
|
+
};
|
|
37
|
+
export declare type AdsConfigBasic = {
|
|
38
|
+
adTagParameters: {
|
|
39
|
+
iu: string;
|
|
40
|
+
cust_params: string;
|
|
41
|
+
};
|
|
42
|
+
};
|
|
43
|
+
export declare type AdsConfigCCPAorAus = AdsConfigBasic & {
|
|
44
|
+
restrictedDataProcessor: boolean;
|
|
45
|
+
};
|
|
46
|
+
export declare type AdsConfigTCFV2 = AdsConfigBasic & {
|
|
47
|
+
adTagParameters: {
|
|
48
|
+
cmpGdpr: number;
|
|
49
|
+
cmpVcd: string;
|
|
50
|
+
cmpGvcd: string;
|
|
51
|
+
};
|
|
52
|
+
nonPersonalizedAd: boolean;
|
|
53
|
+
};
|
|
54
|
+
export declare type AdsConfigEnabled = AdsConfigBasic | AdsConfigCCPAorAus | AdsConfigTCFV2;
|
|
55
|
+
export declare type AdsConfig = AdsConfigEnabled | AdsConfigDisabled;
|
|
56
|
+
export declare type AdTargetingBuilder = () => Promise<AdsConfig>;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@guardian/commercial-core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.26.1",
|
|
4
4
|
"description": "Guardian advertising business logic",
|
|
5
5
|
"homepage": "https://github.com/guardian/commercial-core#readme",
|
|
6
6
|
"bugs": {
|
|
@@ -42,6 +42,7 @@
|
|
|
42
42
|
"devDependencies": {
|
|
43
43
|
"@commitlint/cli": "^13",
|
|
44
44
|
"@commitlint/config-conventional": "^13",
|
|
45
|
+
"@guardian/consent-management-platform": "^8",
|
|
45
46
|
"@guardian/eslint-config-typescript": "^0.7",
|
|
46
47
|
"@guardian/libs": "^3",
|
|
47
48
|
"@guardian/prettier": "^0.7",
|
|
@@ -58,7 +59,7 @@
|
|
|
58
59
|
"eslint-config-prettier": "^8",
|
|
59
60
|
"eslint-plugin-eslint-comments": "^3",
|
|
60
61
|
"eslint-plugin-import": "^2",
|
|
61
|
-
"eslint-plugin-jest": "^
|
|
62
|
+
"eslint-plugin-jest": "^25",
|
|
62
63
|
"eslint-plugin-prettier": "^4",
|
|
63
64
|
"husky": "^7",
|
|
64
65
|
"jest": "^27",
|