Skip to content

Commit

Permalink
✨ Update amp-access-scroll (#26810)
Browse files Browse the repository at this point in the history
* ✨ Update amp-access-scroll

This PR
- adds a protocol version query param to requests made from amp-access-scroll for compatibility
- adds a "holdback" flag and styles to prevent mismatched rendeirng with new layout
- renames ScrollAudio to ScrollSheet
- adds ability to dynamically size horizontal layout for ScrollComponents. Special care
  was taken to ensure these properties cannot interrupt vertical browser layout;
  only width, left, and right are allowed.
  Dynamic sizing is necessary to pervent invisible iframes being drawn over usable
  parts of the underlying page, which would cause UX and accessibility problems in
  certain layout scenarios. Dynamic sizing solves this by shrinking the iframe to fit
  the size neccessary to fit the rendered controls.

* Refactor holdback and iframe title

* Resolve circular dependency

* Simplify scrolltab classes

* Cleanup css

* Use setAttribute

* Update scrollbar dimensions
  • Loading branch information
junoatwork committed Feb 22, 2020
1 parent 99c5984 commit ecfb6c1
Show file tree
Hide file tree
Showing 9 changed files with 279 additions and 86 deletions.
59 changes: 45 additions & 14 deletions extensions/amp-access-scroll/0.1/amp-access-scroll.css
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* Copyright 2018 The AMP HTML Authors. All Rights Reserved.
* Copyright 2020 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.
Expand All @@ -15,34 +15,65 @@
*/

.amp-access-scroll-bar {
height: 44px;
position: fixed;
left: 0;
width: 100%;
background: transparent;
z-index: 2147483647;

bottom: 0;
height: 60px;
left: auto;
right: 0px;
width: 48px;
}

.amp-access-scroll-audio {
position: fixed;
background-color: transparent;
.amp-access-scroll-bar.amp-access-scroll-holdback {
height: 44px;
left: 0;
right: 0;
width: 100%;
}

.amp-access-scroll-sheet {
position: fixed;
background: transparent;
z-index: 2147483647;
display: block;
}

/*
mobile media query needed for !important.
!important is necessary because mobile style
should override inline style.
*/
@media (max-width: 599px) {
.amp-access-scroll-sheet {
/* mobile */
bottom: 60px;
height: 100px;
left: 0 !important;
right: 0 !important;
width: 100% !important;
}
}

@media (min-width: 600px) {
.amp-access-scroll-sheet {
bottom: 76px;
height: 56px;
left: auto;
right: 16px;
width: 475px;
}
}

.amp-access-scroll-sheet.amp-access-scroll-holdback {
/* mobile */
bottom: 44px;
height: 100px;
left: 0;
width: 100%;
}

@media (min-width: 600px) {
.amp-access-scroll-audio {
.amp-access-scroll-sheet.amp-access-scroll-holdback {
bottom: 63px;
height: 56px;
left: auto;
right: 16px;
width: 475px;
}
}
2 changes: 1 addition & 1 deletion extensions/amp-access-scroll/0.1/amp-access-scroll.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* Copyright 2018 The AMP HTML Authors. All Rights Reserved.
* Copyright 2020 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.
Expand Down
2 changes: 1 addition & 1 deletion extensions/amp-access-scroll/0.1/read-depth-tracker.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* Copyright 2019 The AMP HTML Authors. All Rights Reserved.
* Copyright 2020 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.
Expand Down
63 changes: 40 additions & 23 deletions extensions/amp-access-scroll/0.1/scroll-bar.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* Copyright 2019 The AMP HTML Authors. All Rights Reserved.
* Copyright 2020 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.
Expand All @@ -14,6 +14,7 @@
* limitations under the License.
*/

import {PROTOCOL_VERSION} from './scroll-protocol';
import {ScrollComponent} from './scroll-component';
import {dict} from '../../../src/utils/object';

Expand All @@ -28,9 +29,10 @@ class Bar extends ScrollComponent {
* @param {!../../../src/service/ampdoc-impl.AmpDoc} doc
* @param {!../../amp-access/0.1/amp-access-source.AccessSource} accessSource
* @param {string} baseUrl
* @param {boolean} holdback
*/
constructor(doc, accessSource, baseUrl) {
super(doc);
constructor(doc, accessSource, baseUrl, holdback) {
super(doc, holdback);

/** @protected */
this.accessSource_ = accessSource;
Expand All @@ -43,20 +45,20 @@ class Bar extends ScrollComponent {

/** @private */
render_() {
this.mutate_(() => {
this.mutate(() => {
if (!this.frame_) {
this.frame_ = this.makeIframe_();
this.makeIframe_();
this.setWindow_(this.frame_.contentWindow);
}
this.renderHorizontalLayout();
});
}

/**
* @return {!HTMLIFrameElement}
* @protected
* */
makeIframe_() {
const frame = this.el(
this.frame_ = /** @type {!HTMLIFrameElement} */ (this.el(
'iframe',
dict({
'scrolling': 'no',
Expand All @@ -70,53 +72,68 @@ class Bar extends ScrollComponent {
'allow-top-navigation allow-popups ' +
'allow-popups-to-escape-sandbox',
})
);
));

const root = this.el(
this.root_ = this.el(
'div',
dict({
'class': 'amp-access-scroll-bar',
}),
[frame]
[this.frame_]
);

this.mount_(root);
this.toggleClass(this.HOLDBACK_CLASS, this.holdback_);
this.mount();
}

/**
* @param {!JsonObject} action
*/
update(action) {
const changed = this.updateHorizontalLayout(action);

return /** @type {!HTMLIFrameElement} */ (frame);
if (changed) {
this.render_();
}
}
}

export class ScrollUserBar extends Bar {
/**
* Load the scrollbar URL in the iframe.
*
* @protected
* @override
* */
makeIframe_() {
const frame = Bar.prototype.makeIframe_.call(this);
Bar.prototype.makeIframe_.call(this);
// Set iframe to scrollbar URL.
this.accessSource_
.buildUrl(
`${this.baseUrl_}/html/amp/scrollbar` +
`${this.baseUrl_}/html/amp/${
this.holdback_ ? 'scrollbar' : 'scrolltab'
}` +
'?rid=READER_ID' +
'&cid=CLIENT_ID(scroll1)' +
'&c=CANONICAL_URL' +
'&o=AMPDOC_URL',
'&o=AMPDOC_URL' +
`&p=${PROTOCOL_VERSION}`,
false
)
.then(scrollbarUrl => {
frame.setAttribute('src', scrollbarUrl);
this.frame_.setAttribute('src', scrollbarUrl);
});
return frame;
}
}
/**
* Add link to the Scroll App connect page.
*/
export class ActivateBar extends Bar {
/** @override */
/**
* @protected
* @override
* */
makeIframe_() {
const frame = Bar.prototype.makeIframe_.call(this);
Bar.prototype.makeIframe_.call(this);

this.accessSource_
.buildUrl(
Expand All @@ -125,12 +142,12 @@ export class ActivateBar extends Bar {
'&cid=CLIENT_ID(scroll1)' +
'&c=CANONICAL_URL' +
'&o=AMPDOC_URL' +
'&x=QUERY_PARAM(scrollx)',
'&x=QUERY_PARAM(scrollx)' +
`&p=${PROTOCOL_VERSION}`,
false
)
.then(url => {
frame.setAttribute('src', url);
this.frame_.setAttribute('src', url);
});
return frame;
}
}
102 changes: 94 additions & 8 deletions extensions/amp-access-scroll/0.1/scroll-component.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* Copyright 2019 The AMP HTML Authors. All Rights Reserved.
* Copyright 2020 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.
Expand All @@ -15,20 +15,43 @@
*/

import {Services} from '../../../src/services';
import {assertDoesNotContainDisplay, px, setStyles} from '../../../src/style';
import {createElementWithAttributes} from '../../../src/dom';
import {devAssert} from '../../../src/log';
import {hasOwn} from '../../../src/utils/object';

/** @abstract */
export class ScrollComponent {
/** @param {!../../../src/service/ampdoc-impl.AmpDoc} doc */
constructor(doc) {
/**
* @param {!../../../src/service/ampdoc-impl.AmpDoc} doc
* @param {boolean} holdback
*/
constructor(doc, holdback) {
/** @protected {!../../../src/service/ampdoc-impl.AmpDoc} */
this.doc_ = doc;

/** @protected */
this.holdback_ = holdback;

/** @protected @property {?function(Window):undefined} */
this.setWindow_ = null;

/** @protected {?Element} */
this.root_ = null;

/** @protected {?HTMLIFrameElement} */
this.frame_ = null;

/** @protected {ScrollComponent.HorizontalLayout} */
this.layout_ = {
'width': null,
'left': null,
'right': null,
};

/** @protected */
this.HOLDBACK_CLASS = 'amp-access-scroll-holdback';

/** @type {Promise<Window>} */
this.window = new Promise(resolve => {
/** @protected */
Expand Down Expand Up @@ -58,20 +81,83 @@ export class ScrollComponent {

/**
* Add element to doc and promote to fixed layer.
* @param {!Element} el
* @protected
* */
mount_(el) {
this.doc_.getBody().appendChild(el);
Services.viewportForDoc(this.doc_).addToFixedLayer(el);
mount() {
const root = devAssert(this.root_);
this.doc_.getBody().appendChild(root);
Services.viewportForDoc(this.doc_).addToFixedLayer(root);
}

/**
* Enqueues a DOM mutation managed by the window's Vsync
* @param {function():undefined} mutator
* @protected
*/
mutate_(mutator) {
mutate(mutator) {
Services.vsyncFor(this.doc_.win).mutate(mutator);
}

/**
*
* @param {string} className
* @param {boolean} condition
* @protected
*/
toggleClass(className, condition) {
const classes = devAssert(this.root_).classList;
if (condition) {
classes.add(className);
} else {
classes.remove(className);
}
}

/**
* @param {Object} updates
* @return {boolean} true if changed
* @protected
*/
updateHorizontalLayout(updates) {
let changed = false;
// only update styles already set in the layout, updates in place
Object.keys(this.layout_).forEach(key => {
if (!hasOwn(updates, key)) {
return;
}
const size = this.cssSize(updates[key]);
if (this.layout_[key] !== size) {
this.layout_[key] = size;
changed = true;
}
});
return changed;
}

/**
* This method should only be called inside of a mutate() callback.
*
* @protected
*/
renderHorizontalLayout() {
setStyles(devAssert(this.root_), assertDoesNotContainDisplay(this.layout_));
}

/**
* @param {string|number} size
* @return {string}
*/
cssSize(size) {
return typeof size === 'number' ? px(size) : size;
}
}

/**
* Anything affecting vertical layout (height, top, bottom) is ommitted.
* @typedef {{
* width: ?string,
* left: ?string,
* right: ?string
* }}
*/
ScrollComponent.HorizontalLayout;

0 comments on commit ecfb6c1

Please sign in to comment.