diff --git a/blocks/api/categories.js b/blocks/api/categories.js index bb6e3576e92c70..4757cd932531cd 100644 --- a/blocks/api/categories.js +++ b/blocks/api/categories.js @@ -13,7 +13,8 @@ import { __ } from 'i18n'; */ const categories = [ { slug: 'common', title: __( 'Common Blocks' ) }, - { slug: 'layout', title: __( 'Layout Blocks' ) } + { slug: 'layout', title: __( 'Layout Blocks' ) }, + { slug: 'social', title: __( 'Social Media' ) } ]; /** diff --git a/blocks/library/embed/index.js b/blocks/library/embed/index.js index 5e153f33f2a1c6..dbb80fb0dda966 100644 --- a/blocks/library/embed/index.js +++ b/blocks/library/embed/index.js @@ -32,7 +32,7 @@ registerBlock( 'core/embed', { if ( ! url ) { return ( - + diff --git a/blocks/library/index.js b/blocks/library/index.js index 655e7ab40dd43b..c5704219d56752 100644 --- a/blocks/library/index.js +++ b/blocks/library/index.js @@ -8,3 +8,4 @@ import './quote'; import './separator'; import './button'; import './pullquote'; +import './tweet'; diff --git a/blocks/library/tweet/index.js b/blocks/library/tweet/index.js new file mode 100644 index 00000000000000..ba4432ce7effd3 --- /dev/null +++ b/blocks/library/tweet/index.js @@ -0,0 +1,112 @@ +/** + * External dependencies + */ +import jQuery from 'jquery'; +/** + * WordPress dependencies + */ +import Sandbox from 'components/sandbox'; +import Button from 'components/button'; +import Placeholder from 'components/placeholder'; +/** + * Internal dependencies + */ +import { registerBlock } from '../../api'; + +registerBlock( 'core/tweet', { + title: wp.i18n.__( 'Tweet' ), + icon: 'twitter', + + category: 'social', + + attributes( url ) { + return { url }; + }, + + edit: class extends wp.element.Component { + constructor() { + super( ...arguments ); + this.fetchTweet = this.fetchTweet.bind( this ); + // Copies the block's url so we can edit it without having the block + // update (i.e. refetch the tweet) every time it changes in this edit component. + this.state = { + url: this.props.attributes.url, + html: '', + error: false, + fetching: false, + }; + } + doFetch( url ) { + this.setState( { fetching: true, error: false } ); + this.fetchXHR = jQuery.ajax( { + type: 'GET', + dataType: 'jsonp', + timeout: 5000, + url: 'https://publish.twitter.com/oembed?url=' + encodeURI( url ), + error: () => this.setState( { fetching: false, error: true } ), + success: ( msg ) => { + this.props.setAttributes( { url } ); + this.setState( { fetching: false, error: false, html: msg.html } ); + }, + } ); + } + componentDidMount() { + if ( this.state.url ) { + this.doFetch( this.state.url ); + } + } + componentWillUnmount() { + if ( this.fetchXHR ) { + this.fetchXHR.abort(); + delete this.fetchXHR; + } + } + fetchTweet( event ) { + const { url } = this.state; + event.preventDefault(); + this.doFetch( url ); + } + render() { + const { html, url, error, fetching } = this.state; + + if ( html ) { + /* translators: %s: url of the tweet */ + const title = wp.i18n.sprintf( wp.i18n.__( 'Embedded from %s' ), url ); + return ( + + ); + } + + return ( + +
+ this.setState( { url: event.target.value } ) } /> + { ! fetching ? + ( + + ) : ( + + ) + } + + { error && (

{ wp.i18n.__( 'Sorry, we couldn\'t fetch that tweet.' ) }

) } +
+ ); + } + }, + + save( { attributes } ) { + const { url } = attributes; + return url; + } +} ); diff --git a/components/placeholder/style.scss b/components/placeholder/style.scss index d28fea95f215cc..e6414ea3bccfa8 100644 --- a/components/placeholder/style.scss +++ b/components/placeholder/style.scss @@ -25,9 +25,15 @@ .components-placeholder__fieldset { display: flex; flex-direction: row; + flex-wrap: wrap; justify-content: center; width: 100%; max-width: 280px; + + .components-placeholder__error { + font-family: $default-font; + font-size: $default-font-size; + } } .components-placeholder__input { diff --git a/components/resizable-iframe/README.md b/components/resizable-iframe/README.md new file mode 100644 index 00000000000000..b6423601d7a566 --- /dev/null +++ b/components/resizable-iframe/README.md @@ -0,0 +1,46 @@ +Resizable Iframe +================ + +Resizable Iframe is a React component for rendering an `', + '', + + '', + 'https://twitter.com/automattic/status/777261837335326720', + '', ].join( '' ), }, }; diff --git a/webpack.config.js b/webpack.config.js index 80654973dbf514..0a9968259b7f14 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -23,6 +23,7 @@ const config = { react: 'React', 'react-dom': 'ReactDOM', 'react-dom/server': 'ReactDOMServer', + jquery: 'jQuery', tinymce: 'tinymce' }, resolve: {