diff --git a/extensions/amp-twitter/1.0/component.js b/extensions/amp-twitter/1.0/component.js
index 364cb25218378..932c0bef0fbaa 100644
--- a/extensions/amp-twitter/1.0/component.js
+++ b/extensions/amp-twitter/1.0/component.js
@@ -33,8 +33,11 @@ const MATCHES_MESSAGING_ORIGIN = () => true;
* @param {{current: (!TwitterDef.Api|null)}} ref
* @return {PreactDef.Renderable}
*/
-function TwitterWithRef({requestResize, title, ...rest}, ref) {
- const [height, setHeight] = useState(FULL_HEIGHT);
+function TwitterWithRef(
+ {momentid, options, requestResize, style, title, tweetid, ...rest},
+ ref
+) {
+ const [height, setHeight] = useState(null);
const messageHandler = useCallback(
(event) => {
const data = deserializeMessage(event.data);
@@ -60,8 +63,9 @@ function TwitterWithRef({requestResize, title, ...rest}, ref) {
// non-overridable props
matchesMessagingOrigin={MATCHES_MESSAGING_ORIGIN}
messageHandler={messageHandler}
+ options={{tweetid, momentid, ...options}}
type={TYPE}
- wrapperStyle={{height}}
+ style={height ? {...style, height} : style}
/>
);
}
diff --git a/extensions/amp-twitter/1.0/storybook/Basic.js b/extensions/amp-twitter/1.0/storybook/Basic.js
index cd2fd2f5db52a..1c00737254769 100644
--- a/extensions/amp-twitter/1.0/storybook/Basic.js
+++ b/extensions/amp-twitter/1.0/storybook/Basic.js
@@ -16,7 +16,7 @@
import * as Preact from '../../../../src/preact';
import {Twitter} from '../component';
-import {withKnobs} from '@storybook/addon-knobs';
+import {boolean, number, select, withKnobs} from '@storybook/addon-knobs';
export default {
title: 'Twitter',
@@ -25,7 +25,57 @@ export default {
};
export const _default = () => {
+ const tweetId = select(
+ 'tweet id',
+ ['1356304203044499462', '495719809695621121', '463440424141459456'],
+ '1356304203044499462'
+ );
+ const cards = boolean('show cards', true) ? undefined : 'hidden';
+ const conversation = boolean('show conversation', false) ? undefined : 'none';
+ return (
+
+ );
+};
+
+export const moments = () => {
+ const limit = number('limit to', 2);
+ return (
+
+ );
+};
+
+export const timelines = () => {
+ const tweetLimit = number('limit to', 5);
+ const timelineSourceType = select(
+ 'source type',
+ ['profile', 'likes', 'list', 'source', 'collection', 'url', 'widget'],
+ 'profile'
+ );
+ const timelineScreenName = 'amphtml';
+ const timelineUserId = '3450662892';
return (
-
+
);
};
diff --git a/extensions/amp-twitter/1.0/test/test-component.js b/extensions/amp-twitter/1.0/test/test-component.js
new file mode 100644
index 0000000000000..c0ba17d74fd82
--- /dev/null
+++ b/extensions/amp-twitter/1.0/test/test-component.js
@@ -0,0 +1,157 @@
+/**
+ * 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.
+ * 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.
+ */
+
+import * as Preact from '../../../../src/preact';
+import {Twitter} from '../component';
+import {WithAmpContext} from '../../../../src/preact/context';
+import {createRef} from '../../../../src/preact';
+import {mount} from 'enzyme';
+import {serializeMessage} from '../../../../src/3p-frame-messaging';
+import {waitFor} from '../../../../testing/test-helper';
+
+describes.sandboxed('Twitter preact component v1.0', {}, (env) => {
+ it('should render', () => {
+ const wrapper = mount(
+
+ );
+
+ const iframe = wrapper.find('iframe');
+
+ expect(iframe.prop('src')).to.equal(
+ 'http://ads.localhost:9876/dist.3p/current/frame.max.html'
+ );
+ expect(wrapper.find('iframe').prop('style').width).to.equal('100%');
+ expect(wrapper.find('iframe').prop('style').height).to.equal('100%');
+ });
+
+ it('should call given requestResize', () => {
+ const requestResizeSpy = env.sandbox.spy();
+ const wrapper = mount(
+
+ );
+
+ const iframe = wrapper.find('iframe').getDOMNode();
+ const sentinel = JSON.parse(iframe.getAttribute('name'))['attributes'][
+ 'sentinel'
+ ];
+
+ const mockEvent = new CustomEvent('message');
+ mockEvent.data = serializeMessage('embed-size', sentinel, {
+ 'height': 1000,
+ });
+ mockEvent.source = wrapper
+ .getDOMNode()
+ .querySelector('iframe').contentWindow;
+ window.dispatchEvent(mockEvent);
+
+ expect(requestResizeSpy).to.have.been.calledOnce;
+ });
+
+ it('should change height', async () => {
+ const wrapper = mount(
+
+ );
+
+ const iframe = wrapper.find('iframe').getDOMNode();
+ const sentinel = JSON.parse(iframe.getAttribute('name'))['attributes'][
+ 'sentinel'
+ ];
+
+ const mockEvent = new CustomEvent('message');
+ mockEvent.data = serializeMessage('embed-size', sentinel, {
+ 'height': 1000,
+ });
+ mockEvent.source = wrapper
+ .getDOMNode()
+ .querySelector('iframe').contentWindow;
+ window.dispatchEvent(mockEvent);
+
+ wrapper.update();
+
+ await waitFor(
+ () => wrapper.find('div').at(0).prop('style').height == 1000,
+ 'Height changes'
+ );
+
+ expect(wrapper.find('div').at(0).prop('style').height).to.equal(1000);
+ });
+
+ it('should dispatch load event', async () => {
+ const ref = createRef();
+ const onReadyState = env.sandbox.spy();
+ const wrapper = mount(
+
+ );
+
+ let api = ref.current;
+ expect(api.readyState).to.equal('loading');
+ expect(onReadyState).to.not.be.called;
+
+ await wrapper.find('iframe').invoke('onLoad')();
+ api = ref.current;
+ expect(api.readyState).to.equal('complete');
+ expect(onReadyState).to.be.calledOnce.calledWith('complete');
+ });
+
+ it('should reset iframe on pause', () => {
+ const ref = createRef();
+ const wrapper = mount(
+
+
+
+ );
+ expect(wrapper.find('iframe')).to.have.lengthOf(1);
+
+ const iframe = wrapper.find('iframe').getDOMNode();
+ const spy = env.sandbox.spy(iframe, 'src', ['set']);
+ wrapper.setProps({playable: false});
+ expect(spy.set).to.be.calledOnce;
+ });
+});
diff --git a/src/preact/component/3p-frame.js b/src/preact/component/3p-frame.js
index 253695e86376f..8cd8e71aa1958 100644
--- a/src/preact/component/3p-frame.js
+++ b/src/preact/component/3p-frame.js
@@ -62,6 +62,7 @@ const DEFAULT_SANDBOX =
function ProxyIframeEmbedWithRef(
{
allow = BLOCK_SYNC_XHR,
+ bootstrap,
contextOptions,
excludeSandbox,
name: nameProp,
@@ -122,7 +123,7 @@ function ProxyIframeEmbedWithRef(
name: JSON.stringify(
dict({
'host': parseUrlDeprecated(src).hostname,
- 'bootstrap': getBootstrapUrl(type, win),
+ 'bootstrap': bootstrap ?? getBootstrapUrl(type, win),
'type': type,
// "name" must be unique across iframes, so we add a count.
// See: https://github.com/ampproject/amphtml/pull/2955
@@ -132,7 +133,16 @@ function ProxyIframeEmbedWithRef(
),
src,
});
- }, [contextOptions, count, nameProp, options, srcProp, title, type]);
+ }, [
+ bootstrap,
+ contextOptions,
+ count,
+ nameProp,
+ options,
+ srcProp,
+ title,
+ type,
+ ]);
return (