Skip to content

Commit

Permalink
Added CTA Migration page-outlink experiment (ampproject#35867)
Browse files Browse the repository at this point in the history
* Added CTA Migration page-outlink experiment, added logic to have it load during the Promise chain in the layout callback

* fixed some circleCI errors

* fixed some nits

* refactored cta migration and added non-hardcoded values

* fixed cricleci spacing issues

* fixed cricleci spacing issues

* Added description and re-comtting due to circleCI

* Added description and re-comtting due to circleCI

* Fixed more code review nits

* Fixed layout callback unit test

* Added back in code-breaking refactor

* will open new PR to fix page-attachment prod bug

* fixed main merge

* renamed the expirement to make it clear it was associated with ads
  • Loading branch information
jshamble authored and ishaba committed Sep 2, 2021
1 parent eaec7b9 commit bf98a65
Show file tree
Hide file tree
Showing 5 changed files with 216 additions and 17 deletions.
5 changes: 5 additions & 0 deletions examples/amp-story/ads/app-install.html
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,11 @@
<meta name="amp4ads-vars-attribution-icon" content="/test/fixtures/e2e/amphtml-ads/resource/icon.png">
<meta name="amp4ads-vars-attribution-url" content="https://www.google.com">

<meta name="amp4ads-vars-cta-accent-element" content="text">
<meta name="amp4ads-vars-cta-accent-color" content="#FF00FF">
<meta name="amp4ads-vars-cta-image" content="/examples/visual-tests/picsum.photos/image1068_300x169.jpg">
<meta name="amp4ads-vars-theme" content="custom">

<style amp-custom>
@font-face {
font-family: "Poppins";
Expand Down
91 changes: 74 additions & 17 deletions extensions/amp-story-auto-ads/0.1/story-ad-ui.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import {createElementWithAttributes, iterateCursor} from '#core/dom';
import {dict, map} from '#core/types/object';

import {isExperimentOn} from '#experiments';

import {CSS as attributionCSS} from '../../../build/amp-story-auto-ads-attribution-0.1.css';
import {CSS as ctaButtonCSS} from '../../../build/amp-story-auto-ads-cta-button-0.1.css';
import {dev, user} from '../../../src/log';
Expand Down Expand Up @@ -38,6 +40,14 @@ export const A4AVarNames = {
CTA_URL: 'cta-url',
};

/** @type {Array<string>} */
const PageOutlinkLayerVarNames = [
'cta-accent-color',
'cta-accent-element',
'cta-image',
'theme',
];

/** @enum {string} */
const DataAttrs = {
CTA_TYPE: 'data-vars-ctatype',
Expand Down Expand Up @@ -182,6 +192,65 @@ export function handleAttributionClick(win, href) {
openWindowDialog(win, href, '_blank');
}

/**
* Creates a page-outlink element, returns an anchor tag containing relevant data if successful.
* @param {!Document} doc
* @param {!StoryAdUIMetadata} uiMetadata
* @param {!Element} container
* @return {?Element}
*/
function createPageOutlink_(doc, uiMetadata, container) {
const pageOutlink = doc.createElement('amp-story-page-outlink');
pageOutlink.setAttribute('layout', 'nodisplay');

const pageAnchorTag = doc.createElement('a');
pageAnchorTag.href = uiMetadata[A4AVarNames.CTA_URL];
pageAnchorTag.textContent = uiMetadata[A4AVarNames.CTA_TYPE];

pageOutlink.appendChild(pageAnchorTag);

for (const pageOutlinkLayerVarName of PageOutlinkLayerVarNames) {
if (uiMetadata[pageOutlinkLayerVarName]) {
pageOutlink.setAttribute(
pageOutlinkLayerVarName,
uiMetadata[pageOutlinkLayerVarName]
);
}
}

pageOutlink.className = 'i-amphtml-story-page-outlink-container';

container.appendChild(pageOutlink);
return container;
}

/**
* Creates a CTA layer, returns an anchor tag containing relevant data if successful.
* @param {!Element} a
* @param {!Document} doc
* @param {!Element} container
* @return {?Element}
*/
function createCtaLayer_(a, doc, container) {
const ctaLayer = doc.createElement('amp-story-cta-layer');
ctaLayer.className = 'i-amphtml-cta-container';

const linkRoot = createElementWithAttributes(
doc,
'div',
dict({
'class': 'i-amphtml-story-ad-link-root',
'role': 'button',
})
);

createShadowRootWithStyle(linkRoot, a, ctaButtonCSS);

ctaLayer.appendChild(linkRoot);
container.appendChild(ctaLayer);
return a;
}

/**
* @param {!Document} doc
* @param {!./story-ad-button-text-fitter.ButtonTextFitter} buttonFitter
Expand Down Expand Up @@ -223,22 +292,10 @@ export function createCta(doc, buttonFitter, container, uiMetadata) {
return null;
}

const ctaLayer = doc.createElement('amp-story-cta-layer');
ctaLayer.className = 'i-amphtml-cta-container';

const linkRoot = createElementWithAttributes(
doc,
'div',
dict({
'class': 'i-amphtml-story-ad-link-root',
'role': 'button',
})
);

createShadowRootWithStyle(linkRoot, a, ctaButtonCSS);

ctaLayer.appendChild(linkRoot);
container.appendChild(ctaLayer);
return a;
if (isExperimentOn(doc.defaultView, 'amp-story-ads-page-outlink')) {
return createPageOutlink_(doc, uiMetadata, container);
} else {
return createCtaLayer_(a, doc, container);
}
});
}
127 changes: 127 additions & 0 deletions extensions/amp-story-auto-ads/0.1/test/test-story-ad-ui.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import {toggleExperiment} from '#experiments';

import {ButtonTextFitter} from '../story-ad-button-text-fitter';
import {
A4AVarNames,
Expand Down Expand Up @@ -216,4 +218,129 @@ describes.realWin('story-ad-ui', {amp: true}, (env) => {
});
});
});

describe('createCta page outlink element', () => {
let buttonFitter;

beforeEach(() => {
buttonFitter = new ButtonTextFitter(env.ampdoc);
toggleExperiment(env.win, 'amp-story-ads-page-outlink', true, true);
});

it('createCta page outlink custom theme element', () => {
const metadata = {
'cta-accent-color': '#FF00FF',
'cta-accent-element': 'text',
'cta-image':
'/examples/visual-tests/picsum.photos/image1068_300x169.jpg',
'theme': 'custom',
[A4AVarNames.CTA_TYPE]: 'SHOP',
[A4AVarNames.CTA_URL]: 'https://www.cats.com',
};
return createCta(
doc,
buttonFitter,
doc.body /* container */,
metadata
).then((container) => {
expect(container).to.exist;
const containerElem = doc.querySelector(
'.i-amphtml-story-page-outlink-container'
);
expect(containerElem).to.exist;

expect(containerElem.getAttribute('cta-accent-color')).to.equal(
'#FF00FF'
);
expect(containerElem.getAttribute('cta-accent-element')).to.equal(
'text'
);
expect(containerElem.getAttribute('cta-image')).to.equal(
'/examples/visual-tests/picsum.photos/image1068_300x169.jpg'
);
expect(containerElem.getAttribute('theme')).to.equal('custom');
expect(containerElem.children[0].href).to.equal(
'https://www.cats.com/'
);
expect(containerElem.children[0].textContent).to.equal('SHOP');
});
});

it('createCta page outlink light theme element', () => {
const metadata = {
'theme': 'light',
[A4AVarNames.CTA_TYPE]: 'SHOP',
[A4AVarNames.CTA_URL]: 'https://www.cats.com',
};
return createCta(
doc,
buttonFitter,
doc.body /* container */,
metadata
).then((container) => {
expect(container).to.exist;
const containerElem = doc.querySelector(
'.i-amphtml-story-page-outlink-container'
);
expect(containerElem).to.exist;
expect(containerElem.getAttribute('theme')).to.equal('light');
expect(containerElem.children[0].href).to.equal(
'https://www.cats.com/'
);
expect(containerElem.children[0].textContent).to.equal('SHOP');
});
});

it('createCta page outlink dark theme element', () => {
const metadata = {
'theme': 'dark',
[A4AVarNames.CTA_TYPE]: 'SHOP',
[A4AVarNames.CTA_URL]: 'https://www.cats.com',
};
return createCta(
doc,
buttonFitter,
doc.body /* container */,
metadata
).then((container) => {
expect(container).to.exist;
const containerElem = doc.querySelector(
'.i-amphtml-story-page-outlink-container'
);
expect(containerElem).to.exist;
expect(containerElem.getAttribute('theme')).to.equal('dark');
expect(containerElem.children[0].href).to.equal(
'https://www.cats.com/'
);
expect(containerElem.children[0].textContent).to.equal('SHOP');
});
});

it('createCta page outlink dark theme element with color and accent-element (should have no effect)', () => {
const metadata = {
'theme': 'dark',
'cta-accent-color': '#FF00FF',
'cta-accent-element': 'text',
[A4AVarNames.CTA_TYPE]: 'SHOP',
[A4AVarNames.CTA_URL]: 'https://www.cats.com',
};
return createCta(
doc,
buttonFitter,
doc.body /* container */,
metadata
).then((container) => {
expect(container).to.exist;
const containerElem = doc.querySelector(
'.i-amphtml-story-page-outlink-container'
);
expect(containerElem).to.exist;
expect(containerElem.getAttribute('theme')).to.equal('dark');
expect(containerElem.children[0].href).to.equal(
'https://www.cats.com/'
);
expect(containerElem.children[0].textContent).to.equal('SHOP');
});
});
});
});
3 changes: 3 additions & 0 deletions extensions/amp-story/1.0/amp-story-page.js
Original file line number Diff line number Diff line change
Expand Up @@ -704,6 +704,9 @@ export class AmpStoryPage extends AMP.BaseElement {

/** @return {!Promise} */
beforeVisible() {
// Ensures a dynamically added page-attachment or page-outlink element is built.
// This happens by amp-story-ads.
this.renderOpenAttachmentUI_();
return this.maybeApplyFirstAnimationFrameOrFinish();
}

Expand Down
7 changes: 7 additions & 0 deletions extensions/amp-story/1.0/test/test-amp-story-page.js
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,13 @@ describes.realWin('amp-story-page', {amp: {extensions}}, (env) => {
expect(spy).to.have.been.calledOnce;
});

it('should call renderOpenAttachmentUI_ in beforeVisible', async () => {
const spy = env.sandbox.spy(page, 'renderOpenAttachmentUI_');
page.buildCallback();
await page.layoutCallback();
expect(spy).to.have.been.calledTwice;
});

it('should mark page as loaded after media is loaded', async () => {
const waitForMediaLayoutSpy = env.sandbox.spy(page, 'waitForMediaLayout_');
const markPageAsLoadedSpy = env.sandbox.spy(page, 'markPageAsLoaded_');
Expand Down

0 comments on commit bf98a65

Please sign in to comment.