-
Notifications
You must be signed in to change notification settings - Fork 4.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add tweet block for embedding tweets #754
Conversation
3cd38d1
to
abc74cd
Compare
This commit polishes the Placeholder component to better accommodate error messages. Also rephrases a little verbiage in both the embed and Tweet blocks, and fixes a typo in a CSS class.
…to add/twitter-block
Ah yes, I pushed up changes forgetting that you might have already cloned... muscle memory from old working patterns using Gerrit :) This looks great, thank you! I'm going to go through the code again and prepare it for review. |
Updated the header on the imported resizable iframe component
blocks/library/embed/index.js
Outdated
@@ -31,7 +31,7 @@ registerBlock( 'core/embed', { | |||
if ( ! url ) { | |||
return ( | |||
<Placeholder icon="cloud" label={ wp.i18n.__( 'Embed URL' ) } className="blocks-embed"> | |||
<input type="url" className="placeholder__input" placeholder={ wp.i18n.__( 'Enter URL to embed here...' ) } /> | |||
<input type="url" className="components-placeholder__input" placeholder={ wp.i18n.__( 'Enter URL to embed...' ) } /> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It was already there, but we should update the translated text to use the ellipsis character …
instead of three dots.
blocks/library/tweet/index.js
Outdated
* Internal dependencies | ||
*/ | ||
import { registerBlock, query } from '../../api'; | ||
import Sandbox from '../../../components/sandbox'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
With the components directory, the idea becomes that we treat these more like external modules, but unlike npm modules, they're first-party in the sense that they'd be WordPress-maintained.
So two things:
- We can import this from
components/sandbox
- We should move this under a separate
WordPress dependencies
docblock above this one
There's some more context documented here, which I'm sensing could probably be moved somewhere more obvious:
https://github.com/WordPress/gutenberg/blob/master/docs/coding-guidelines.md#javascript
Also: #716
blocks/library/tweet/index.js
Outdated
} | ||
doFetch( url, setAttributes, setState ) { | ||
setState( { fetching: true, error: false } ); | ||
jQuery.ajax( { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, this could be related to #691. Generally I tend to discourage network requests in the component itself. The line is blurred here because the result of this request is not really valuable outside the context of the component.
The use of jQuery
also sparked my interest. It's handy and readily accessible. We'll probably want to think on our options here some more; probably outside the scope of this pull request specifically.
At the very least, with mind toward #742 and eliminating globals*, we'll probably want to import this as a module and configure Webpack to treat this as an external (reference). I believe it should be enough to add 'jquery': 'jQuery'
there, and then you can import jQuery from 'jquery';
at the top of this file.
* For consistency and to enable changing the source from which jQuery is resolved.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, this is a little yucky, but it's purely in the editor, and only used to show the tweet as it would appear in the post. The saved html is actually just the tweet's url, as wp will process that anyway, doing the same call this javascript does.
I'll address the globals issue.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm also a bit conflicted with relying on jQuery here. It's true we'll have it available in WordPress for the foreseeable future, but coupling an async request with a whole library doesn't feel great for the longer term and portability of editor code.
blocks/library/tweet/index.js
Outdated
super( ...arguments ); | ||
this.fetchTweet = this.fetchTweet.bind( this ); | ||
this.state = { | ||
url: this.props.attributes.url, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's discouraged to create copies of props in state because it muddies the fact that the source of truth is the prop itself, leaving you responsible for maintaining when that prop changes. And evidenced by a lack of componentWillReceiveProps
or componentDidUpdate
, it appears we're not.
Instead, where you're currently using this.state.url
, you should try to reference this.props.attributes.url
directly instead.
And depending on if we want the component to trigger another request if the URL ever changes, it'd be good to bind to componentDidUpdate
to check if the URL has changed and fire doFetch
again.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So when the button is clicked (or form submitted) I'd use a reference to the text input to set the url, instead of having it set when the text input changes?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So when the button is clicked (or form submitted) I'd use a reference to the text input to set the url, instead of having it set when the text input changes?
Ah, I think I overlooked that. With that in mind, it can be fine to create a copy.
Here's an archived (some outdated syntax) article which explains more of the context and highlights this exception:
However, it's not an anti-pattern if you make it clear that the prop is only seed data for the component's internally-controlled state:
Since we don't have as much control over the naming of the incoming prop, except if we were to create an intermediary component or rename the attribute (both of which don't seem ideal), I think it'd be fine to ignore the initialUrl
recommendation here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll add a comment in the constructor to make it clear what we're doing here.
blocks/library/tweet/index.js
Outdated
( | ||
<Button | ||
isLarge | ||
onClick={ this.fetchTweet }> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What if the user types a URL in the text input and hits enter? Instead of binding to onClick
, I'd recommend considering wrapping the input and button in a <form>
, changing the button to type="submit"
, then binding to the forms onSubmit
handler.
components/resizable-iframe/index.js
Outdated
const debug = debugFactory( 'gutenburg:resizable-iframe' ), | ||
noop = () => {}; | ||
|
||
export default React.createClass( { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
React.createClass
is deprecated and this needs to be rewritten as a wp.element.Component
class.
components/resizable-iframe/index.js
Outdated
return { | ||
onLoad: noop, | ||
onResize: noop, | ||
title: uuid() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's the value of a randomly generated title?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
eslint flags an error unless all iframes have a unique title. This seemed the simplest way to get that, unless there's some ID unique to each instance that I can grab? (It wouldn't surprise me, but I couldn't see one)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, that's a confusing rule description:
<iframe>
elements must have a unique title property to indicate its content to the user.
https://github.com/evcohen/eslint-plugin-jsx-a11y/blob/master/docs/rules/iframe-has-title.md
The value of the rule is not in ensuring guaranteed uniqueness, but rather that users of screen readers are easily able to identify and distinguish the contents of the iframe.
I expect the ESLint rule won't enforce its uniqueness, only that it's present.
To the purpose of the rule, I'd say we should set the title from the Twitter block as something like "Twitter Tweet embed". With that in mind, ResizableIframe
should probably accept title
as a prop and apply it to the rendered iframe
.
The referenced guideline has some more useful context: https://dequeuniversity.com/rules/axe/1.1/frame-title
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe also cc @afercia on this one, in case I'm misspeaking. The guidelines do seem to strongly encourage that "no titles are repeated". We could include the Tweet's URL in the title as added uniqueness, but I don't sense this is valuable for screen reader users. The oEmbed response does include some details, like Tweet author and the raw html
(from which the tweet's content could be parsed, though not easily).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Even the tweet author doesn't help that much, if for example, you're embedded two tweets by the same author... Perhaps "Embedded tweet from " is a good compromise though? It would be easy enough to parse that out of the url.
components/sandbox/index.js
Outdated
/** | ||
* External dependencies | ||
*/ | ||
import React from 'react'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Similar issues in this file as the previous; wp.element
in place of React
and avoid React.createClass
(it's not available in wp.element
anyways).
components/sandbox/index.js
Outdated
displayName: 'Sandbox', | ||
|
||
propTypes: { | ||
html: React.PropTypes.string, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
PropTypes
is moved out of React as of version 16. I'd expect this is logging a warning in the console. We should probably just drop propTypes
here altogether.
post-content.js
Outdated
'<!-- /wp:core/embed -->', | ||
|
||
'<!-- wp:core/tweet -->', | ||
'<div>https://twitter.com/automattic/status/777261837335326720</div>', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Per previous comment about returning rawContent
as the block's url
attribute, we should first remove the div
wrapper.
New commit pushed up, should address all the issues. |
This is getting superseded by
#816 , which uses server side
rendering for embedded content, and doesn't use jQuery at all.
…On Wed, May 17, 2017 at 10:41 AM, Matias Ventura ***@***.***> wrote:
***@***.**** commented on this pull request.
------------------------------
In blocks/library/tweet/index.js
<#754 (comment)>:
> + },
+
+ edit: class extends wp.element.Component {
+ constructor() {
+ super( ...arguments );
+ this.fetchTweet = this.fetchTweet.bind( this );
+ this.state = {
+ url: this.props.attributes.url,
+ html: '',
+ error: false,
+ fetching: false,
+ };
+ }
+ doFetch( url, setAttributes, setState ) {
+ setState( { fetching: true, error: false } );
+ jQuery.ajax( {
I'm also a bit conflicted with relying on jQuery here. It's true we'll
have it available in WordPress for the foreseeable future, but coupling an
async request with a whole library doesn't feel great for the longer term
and portability of editor code.
—
You are receiving this because you authored the thread.
Reply to this email directly, view it on GitHub
<#754 (comment)>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/ADKSsodx6LZdAyC2D52gEEb9OzIOj5vWks5r6sCvgaJpZM4NWrde>
.
|
@notnownikki should this one be closed then? |
Yes, closing it now, was just getting the other PR into a working state so work could carry on there. Superseded by #816 |
@jasmussen here you go!