diff --git a/extensions/amp-bind/0.1/bind-validator.js b/extensions/amp-bind/0.1/bind-validator.js
index 2202b1189a93..079667442db1 100644
--- a/extensions/amp-bind/0.1/bind-validator.js
+++ b/extensions/amp-bind/0.1/bind-validator.js
@@ -313,6 +313,9 @@ function createElementRules_() {
'datetime': null,
'title': null,
},
+ 'AMP-TWITTER': {
+ 'data-tweetid': null,
+ },
'AMP-VIDEO': {
'alt': null,
'attribution': null,
diff --git a/extensions/amp-bind/amp-bind.md b/extensions/amp-bind/amp-bind.md
index 62aa015fbe7a..4293f4458478 100644
--- a/extensions/amp-bind/amp-bind.md
+++ b/extensions/amp-bind/amp-bind.md
@@ -488,6 +488,11 @@ Only binding to the following components and attributes are allowed:
[src] |
Fetches JSON from the new URL and merges it into the existing state. Note the following update will ignore <amp-state> elements to prevent cycles. |
+
+ <amp-twitter> |
+ [data-tweetid] |
+ Changes the displayed Tweet. |
+
<amp-video> |
[alt]
[attribution]
[controls]
[loop]
[poster]
[preload]
[src] |
diff --git a/extensions/amp-twitter/0.1/amp-twitter.js b/extensions/amp-twitter/0.1/amp-twitter.js
index 2cd66bfc537a..4aad5a13d67c 100644
--- a/extensions/amp-twitter/0.1/amp-twitter.js
+++ b/extensions/amp-twitter/0.1/amp-twitter.js
@@ -120,6 +120,7 @@ class AmpTwitter extends AMP.BaseElement {
*/
updateForSuccessState_(height) {
this.mutateElement(() => {
+ this.toggleLoading(false);
if (this.userPlaceholder_) {
this.togglePlaceholder(false);
}
@@ -128,7 +129,7 @@ class AmpTwitter extends AMP.BaseElement {
}
/**
- * Updates wheen the tweet that failed to load. This uses the fallback
+ * Updates when the tweet failed to load. This uses the fallback
* provided if available. If not, it uses the user specified placeholder.
* @private
*/
@@ -137,6 +138,7 @@ class AmpTwitter extends AMP.BaseElement {
const content = fallback || this.userPlaceholder_;
this.mutateElement(() => {
+ this.toggleLoading(false);
if (fallback) {
this.togglePlaceholder(false);
this.toggleFallback(true);
@@ -148,6 +150,14 @@ class AmpTwitter extends AMP.BaseElement {
});
}
+ /**
+ * amp-twitter reuses the loading indicator when id changes via bind mutation
+ * @override
+ */
+ isLoadingReused() {
+ return true;
+ }
+
/** @override */
createLoaderLogoCallback() {
const html = htmlFor(this.element);
@@ -182,6 +192,15 @@ class AmpTwitter extends AMP.BaseElement {
}
return true;
}
+
+ /** @override */
+ mutatedAttributesCallback(mutations) {
+ if (this.iframe_ && mutations['data-tweetid'] != null) {
+ this.unlayoutCallback();
+ this.toggleLoading(true, /* opt_force */ true);
+ this.layoutCallback();
+ }
+ }
}
AMP.extension('amp-twitter', '0.1', AMP => {
diff --git a/extensions/amp-twitter/0.1/test/test-amp-twitter.js b/extensions/amp-twitter/0.1/test/test-amp-twitter.js
index 455bc8198053..ba5bdc0f1b46 100644
--- a/extensions/amp-twitter/0.1/test/test-amp-twitter.js
+++ b/extensions/amp-twitter/0.1/test/test-amp-twitter.js
@@ -128,6 +128,18 @@ describes.realWin(
});
});
+ it('should replace iframe after tweetid mutation', () => {
+ const newTweetId = '638793490521001985';
+ return getAmpTwitter(tweetId).then(ampTwitter => {
+ const spy = sandbox.spy(ampTwitter.implementation_, 'toggleLoading');
+ const iframe = ampTwitter.querySelector('iframe');
+ ampTwitter.setAttribute('data-tweetid', newTweetId);
+ ampTwitter.mutatedAttributesCallback({'data-tweetid': newTweetId});
+ expect(spy).to.be.calledWith(true);
+ expect(ampTwitter.querySelector('iframe')).to.not.equal(iframe);
+ });
+ });
+
describe('cleanupTweetId_', () => {
it('does not affect valid tweet ids', () => {
const good = '585110598171631616';