Skip to content
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

Closed
wants to merge 13 commits into from
Closed

Conversation

notnownikki
Copy link
Member

@jasmussen here you go!

@notnownikki notnownikki added the [Status] In Progress Tracking issues with work in progress label May 10, 2017
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.
@jasmussen
Copy link
Contributor

Added some polish to this. Looks good!

As you can see I had a little merge adventure where for some reason I didn't have the latest files. Hopefully that all worked out.

screen shot 2017-05-10 at 18 29 13

@notnownikki
Copy link
Member Author

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
@notnownikki notnownikki changed the title Tweet block - WORK IN PROGESS! Don't merge or review or anything. Add tweet block for embedding tweets May 10, 2017
@notnownikki notnownikki added [Feature] Blocks Overall functionality of blocks and removed [Status] In Progress Tracking issues with work in progress labels May 10, 2017
@@ -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...' ) } />
Copy link
Member

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.

* Internal dependencies
*/
import { registerBlock, query } from '../../api';
import Sandbox from '../../../components/sandbox';
Copy link
Member

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

}
doFetch( url, setAttributes, setState ) {
setState( { fetching: true, error: false } );
jQuery.ajax( {
Copy link
Member

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.

Copy link
Member Author

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.

Copy link
Member

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.

super( ...arguments );
this.fetchTweet = this.fetchTweet.bind( this );
this.state = {
url: this.props.attributes.url,
Copy link
Member

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.

Copy link
Member Author

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?

Copy link
Member

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:

https://github.com/facebook/react/blob/8cac523/docs/tips/10-props-in-getInitialState-as-anti-pattern.md

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.

Copy link
Member Author

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.

(
<Button
isLarge
onClick={ this.fetchTweet }>
Copy link
Member

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.

const debug = debugFactory( 'gutenburg:resizable-iframe' ),
noop = () => {};

export default React.createClass( {
Copy link
Member

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.

return {
onLoad: noop,
onResize: noop,
title: uuid()
Copy link
Member

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?

Copy link
Member Author

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)

Copy link
Member

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

Copy link
Member

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).

Copy link
Member Author

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.

/**
* External dependencies
*/
import React from 'react';
Copy link
Member

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).

displayName: 'Sandbox',

propTypes: {
html: React.PropTypes.string,
Copy link
Member

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>',
Copy link
Member

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.

@notnownikki
Copy link
Member Author

New commit pushed up, should address all the issues.

@notnownikki
Copy link
Member Author

notnownikki commented May 17, 2017 via email

@mtias
Copy link
Member

mtias commented May 17, 2017

@notnownikki should this one be closed then?

@mtias mtias added the [Status] In Progress Tracking issues with work in progress label May 17, 2017
@notnownikki
Copy link
Member Author

Yes, closing it now, was just getting the other PR into a working state so work could carry on there.

Superseded by #816

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
[Feature] Blocks Overall functionality of blocks [Status] In Progress Tracking issues with work in progress
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants