Skip to content

Commit

Permalink
✨Move Story Ads CTA to shadow (#32850)
Browse files Browse the repository at this point in the history
* move to shadow

* tests

* typo
  • Loading branch information
calebcordry committed Feb 24, 2021
1 parent db942db commit 775b5f6
Show file tree
Hide file tree
Showing 9 changed files with 119 additions and 61 deletions.
1 change: 1 addition & 0 deletions build-system/compile/bundles.config.extensions.json
Expand Up @@ -454,6 +454,7 @@
"cssBinaries": [
"amp-story-auto-ads-ad-badge",
"amp-story-auto-ads-attribution",
"amp-story-auto-ads-cta-button",
"amp-story-auto-ads-inabox",
"amp-story-auto-ads-shared"
]
Expand Down
@@ -0,0 +1,60 @@
/**
* Copyright 2021 The AMP HTML Authors. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS-IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

/* If you are changing anything here that affects font, please update measurer
in story-ad-button-text-fitter.js */
.i-amphtml-story-ad-link {
background-color: #ffffff !important;
border-radius: 20px !important;
box-sizing: border-box !important;
bottom: 32px !important;
box-shadow: 0px 2px 12px rgba(0, 0, 0, 0.16) !important;
color: #4285f4 !important;
font-family: 'Roboto', sans-serif !important;
font-weight: bold !important;
height: 36px !important;
letter-spacing: 0.2px !important;
line-height: 36px !important;
overflow: hidden !important;
/* no important on opacity b/c ff does not allow keyframes to override. */
opacity: 0;
padding: 0 10px !important;
position: absolute !important;
text-align: center !important;
text-decoration: none !important;
min-width: 120px !important;
max-width: calc(100vw - 64px);
}

[cta-active].i-amphtml-story-ad-link {
animation-delay: 100ms !important;
animation-duration: 300ms !important;
animation-timing-function: cubic-bezier(0.4, 0, 0.2, 1) !important;
animation-fill-mode: forwards !important;
animation-name: ad-cta !important;
}

@keyframes ad-cta {
from {
opacity: 0;
transform: scale(0);
}

to {
opacity: 1;
transform: scale(1);
}
}
Expand Up @@ -14,7 +14,7 @@
* limitations under the License.
*/

amp-story-cta-layer {
amp-story-cta-layer {
display: block !important;
position: absolute !important;
top: 80% !important;
Expand Down
52 changes: 5 additions & 47 deletions extensions/amp-story-auto-ads/0.1/amp-story-auto-ads-shared.css
Expand Up @@ -16,54 +16,12 @@

/* CSS shared between amp-story-auto-ads and inabox story ad rendering. */

.i-amphtml-cta-container {
.i-amphtml-story-ad-link-root {
all: initial !important;
color: initial !important;
display: flex !important;
flex-direction: column !important;
align-items: center !important;
}

/* If you are changing anything here that affects font, please update measurer
in story-ad-button-text-fitter.js */
.i-amphtml-story-ad-link {
background-color: #FFFFFF !important;
border-radius: 20px !important;
box-sizing: border-box !important;
bottom: 32px !important;
box-shadow: 0px 2px 12px rgba(0, 0, 0, 0.16) !important;
color: #4285F4 !important;
font-family: 'Roboto', sans-serif !important;
font-weight: bold !important;
height: 36px !important;
letter-spacing: 0.2px !important;
line-height: 36px !important;
overflow: hidden !important;
/* no important on opacity b/c ff does not allow keyframes to override. */
opacity: 0;
padding: 0 10px !important;
position: absolute !important;
text-align: center !important;
text-decoration: none !important;
min-width: 120px !important;
max-width: calc(100vw - 64px);
}

amp-story-page[active] .i-amphtml-story-ad-link,
.i-amphtml-inabox .i-amphtml-story-ad-link {
animation-delay: 100ms !important;
animation-duration: 300ms !important;
animation-timing-function: cubic-bezier(0.4, 0.0, 0.2, 1) !important;
animation-fill-mode: forwards !important;
animation-name: ad-cta !important;
}

@keyframes ad-cta {
from {
opacity: 0;
transform: scale(0);
}

to {
opacity: 1;
transform: scale(1);
}
height: 100% !important;
width: 100% !important;
}
6 changes: 0 additions & 6 deletions extensions/amp-story-auto-ads/0.1/amp-story-auto-ads.css
Expand Up @@ -19,12 +19,6 @@
transform: scale(1) translateX(-100%) translateY(200%) !important;
}

.i-amphtml-cta-container {
display: flex !important;
flex-direction: column !important;
align-items: center !important;
}

.i-amphtml-story-desktop-fullbleed .i-amphtml-cta-container {
/* Ad will be 75vh, align bottom of container to bottom of ad. */
bottom: 12.5vh !important;
Expand Down
8 changes: 8 additions & 0 deletions extensions/amp-story-auto-ads/0.1/story-ad-page.js
Expand Up @@ -16,6 +16,7 @@

import {
A4AVarNames,
START_CTA_ANIMATION_ATTR,
createCta,
getStoryAdMetadataFromDoc,
getStoryAdMetadataFromElement,
Expand Down Expand Up @@ -111,6 +112,9 @@ export class StoryAdPage {
/** @private {?Element} */
this.adChoicesIcon_ = null;

/** @private {?Element} */
this.ctaAnchor_ = null;

/** @private {?Document} */
this.adDoc_ = null;

Expand Down Expand Up @@ -179,6 +183,9 @@ export class StoryAdPage {
*/
toggleVisibility() {
this.viewed_ = true;
this.ctaAnchor_ &&
toggleAttribute(this.ctaAnchor_, START_CTA_ANIMATION_ATTR);

// TODO(calebcordry): Properly handle visible attribute for custom ads.
if (this.adDoc_) {
toggleAttribute(
Expand Down Expand Up @@ -407,6 +414,7 @@ export class StoryAdPage {
uiMetadata
).then((anchor) => {
if (anchor) {
this.ctaAnchor_ = anchor;
// Click listener so that we can fire `story-ad-click` analytics trigger at
// the appropriate time.
anchor.addEventListener('click', () => {
Expand Down
17 changes: 15 additions & 2 deletions extensions/amp-story-auto-ads/0.1/story-ad-ui.js
Expand Up @@ -22,6 +22,7 @@ import {
openWindowDialog,
} from '../../../src/dom';
import {createShadowRootWithStyle} from '../../amp-story/1.0/utils';
import {CSS as ctaButtonCSS} from '../../../build/amp-story-auto-ads-cta-button-0.1.css';
import {dev, user} from '../../../src/log';
import {dict, map} from '../../../src/utils/object';

Expand All @@ -45,6 +46,8 @@ const CTA_META_PREFIX = 'amp-cta-';
/** @const {string} */
const A4A_VARS_META_PREFIX = 'amp4ads-vars-';

export const START_CTA_ANIMATION_ATTR = 'cta-active';

/** @enum {string} */
export const A4AVarNames = {
ATTRIBUTION_ICON: 'attribution-icon',
Expand Down Expand Up @@ -208,7 +211,6 @@ export function createCta(doc, buttonFitter, container, uiMetadata) {
const ctaUrl = uiMetadata[A4AVarNames.CTA_URL];
const ctaText = uiMetadata[A4AVarNames.CTA_TYPE];

// TODO(ccordry): Move button to shadow root.
const a = createElementWithAttributes(
doc,
'a',
Expand Down Expand Up @@ -241,7 +243,18 @@ export function createCta(doc, buttonFitter, container, uiMetadata) {

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

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

createShadowRootWithStyle(linkRoot, a, ctaButtonCSS);

ctaLayer.appendChild(linkRoot);
container.appendChild(ctaLayer);
return a;
});
Expand Down
24 changes: 20 additions & 4 deletions extensions/amp-story-auto-ads/0.1/test/test-story-ad-page.js
Expand Up @@ -215,6 +215,24 @@ describes.realWin('story-ad-page', {amp: true}, (env) => {
storyAdPage.toggleVisibility();
expect(altBody).not.to.have.attribute('amp-story-visible');
});

it('should add/remove the cta-active signal', async () => {
const pageElement = storyAdPage.build();
// Stub delegateVideoAutoplay.
pageElement.getImpl = () => Promise.resolve(pageImplMock);
doc.body.appendChild(pageElement);

const ampAdElement = doc.querySelector('amp-ad');
ampAdElement.setAttribute('data-vars-ctaurl', 'https://cats.example');
ampAdElement.setAttribute('data-vars-ctatype', 'INSTALL');
await storyAdPage.maybeCreateCta();

const anchor = doc.querySelector('.i-amphtml-story-ad-link');
expect(anchor).to.exist;
expect(anchor).not.to.have.attribute('cta-active');
storyAdPage.toggleVisibility();
expect(anchor).to.have.attribute('cta-active');
});
});

describe('#maybeCreateCta', () => {
Expand All @@ -239,8 +257,7 @@ describes.realWin('story-ad-page', {amp: true}, (env) => {

const ctaLayer = doc.querySelector('amp-story-cta-layer');
expect(ctaLayer).to.exist;
const anchor = ctaLayer.firstChild;
expect(anchor.tagName).to.equal('A');
const anchor = ctaLayer.querySelector('a');
expect(anchor.target).to.equal('_blank');
expect(anchor.href).to.equal('https://amp.dev/');
expect(anchor).to.have.attribute(
Expand All @@ -260,8 +277,7 @@ describes.realWin('story-ad-page', {amp: true}, (env) => {

const ctaLayer = doc.querySelector('amp-story-cta-layer');
expect(ctaLayer).to.exist;
const anchor = ctaLayer.firstChild;
expect(anchor.tagName).to.equal('A');
const anchor = ctaLayer.querySelector('a');
expect(anchor.target).to.equal('_blank');
expect(anchor.href).to.equal('https://amp.dev/');
expect(anchor).to.have.attribute(
Expand Down
10 changes: 9 additions & 1 deletion src/inabox/inabox-story-ad.js
Expand Up @@ -16,6 +16,7 @@

import {ButtonTextFitter} from '../../extensions/amp-story-auto-ads/0.1/story-ad-button-text-fitter';
import {
START_CTA_ANIMATION_ATTR,
createCta,
getStoryAdMetadataFromDoc,
maybeCreateAttribution,
Expand All @@ -41,7 +42,14 @@ export function maybeRenderInaboxAsStoryAd(ampdoc) {

const buttonFitter = new ButtonTextFitter(ampdoc);
const ctaContainer = doc.createElement('div');
createCta(doc, buttonFitter, ctaContainer, storyAdMetadata);

// TODO(ccordry): maybe use inOb for visible signal to fire animation
// across amp & inabox environments.
createCta(doc, buttonFitter, ctaContainer, storyAdMetadata).then(
(ctaAnchor) =>
ctaAnchor && ctaAnchor.setAttribute(START_CTA_ANIMATION_ATTR, '')
);

doc.body.appendChild(ctaContainer);

if (win.parent) {
Expand Down

0 comments on commit 775b5f6

Please sign in to comment.