Skip to content

Commit

Permalink
Bento: Prepare Twitter Preact implementation (#34194)
Browse files Browse the repository at this point in the history
* Do not override user given height until message gives one

* Support tweetid directly

* Support bootstrap directly

* Add unit tests

* Support momentid directly

* Add Storybook samples for additional options

* Use Sinon syntax for spying on setter
  • Loading branch information
caroqliu committed May 6, 2021
1 parent d74de58 commit e181aae
Show file tree
Hide file tree
Showing 4 changed files with 228 additions and 7 deletions.
10 changes: 7 additions & 3 deletions extensions/amp-twitter/1.0/component.js
Expand Up @@ -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);
Expand All @@ -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}
/>
);
}
Expand Down
54 changes: 52 additions & 2 deletions extensions/amp-twitter/1.0/storybook/Basic.js
Expand Up @@ -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',
Expand All @@ -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 (
<Twitter
bootstrap="https://3p.ampproject.net/2104170104001/f.js"
options={{cards, conversation}}
tweetid={tweetId}
src="https://d-41929527682976137678.ampproject.net/2104170104001/frame.html"
style={{width: '300px', height: '200px'}}
/>
);
};

export const moments = () => {
const limit = number('limit to', 2);
return (
<Twitter
bootstrap="https://3p.ampproject.net/2104170104001/f.js"
options={{limit}}
momentid="1009149991452135424"
src="https://d-41929527682976137678.ampproject.net/2104170104001/frame.html"
style={{width: '300px', height: '200px'}}
/>
);
};

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 (
<Twitter style={{width: 300, height: 200}} tweetId="638793490521001985" />
<Twitter
bootstrap="https://3p.ampproject.net/2104170104001/f.js"
options={{
tweetLimit,
timelineSourceType,
timelineScreenName,
timelineUserId,
}}
src="https://d-41929527682976137678.ampproject.net/2104170104001/frame.html"
style={{width: '300px', height: '200px'}}
/>
);
};
157 changes: 157 additions & 0 deletions 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(
<Twitter
tweetid="1356304203044499462"
style={{
'width': '500px',
'height': '600px',
}}
/>
);

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(
<Twitter
tweetid="1356304203044499462"
style={{
'width': '500px',
'height': '600px',
}}
requestResize={requestResizeSpy}
/>
);

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(
<Twitter
tweetid="1356304203044499462"
style={{
'width': '500px',
'height': '600px',
}}
/>
);

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(
<Twitter
ref={ref}
tweetid="1356304203044499462"
style={{
'width': '500px',
'height': '600px',
}}
onReadyState={onReadyState}
/>
);

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(
<WithAmpContext playable={true}>
<Twitter
ref={ref}
tweetid="1356304203044499462"
style={{
'width': '500px',
'height': '600px',
}}
/>
</WithAmpContext>
);
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;
});
});
14 changes: 12 additions & 2 deletions src/preact/component/3p-frame.js
Expand Up @@ -62,6 +62,7 @@ const DEFAULT_SANDBOX =
function ProxyIframeEmbedWithRef(
{
allow = BLOCK_SYNC_XHR,
bootstrap,
contextOptions,
excludeSandbox,
name: nameProp,
Expand Down Expand Up @@ -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
Expand All @@ -132,7 +133,16 @@ function ProxyIframeEmbedWithRef(
),
src,
});
}, [contextOptions, count, nameProp, options, srcProp, title, type]);
}, [
bootstrap,
contextOptions,
count,
nameProp,
options,
srcProp,
title,
type,
]);

return (
<IframeEmbed
Expand Down

0 comments on commit e181aae

Please sign in to comment.