-
Notifications
You must be signed in to change notification settings - Fork 4.7k
Plugin: Quote Block Initial Attempt #327
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
Conversation
youknowriad
left a comment
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.
Thanks for helping on the blocks 👍 I left some small notes above :). The main thing we should reconsider here is avoiding creation of Editable components for each block, we should reuse the same Editable component for all blocks to ease creating blocks.
blocks/index.js
Outdated
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 think our idea for now, is to use the Editable component in all the places we need something to be editable. Typically the Editable component could leverage TinyMCE or something. So in the context of the quote for example, you'd use the Editable twice, once for the content and once for the citation. (I think the quote block from the per-block prototype is using this approach, could be an inspiration https://github.com/WordPress/gutenberg/blob/master/docs/tinymce-per-block/src/blocks/quote-block/form.js#L95-L133)
editor/blocks/quote-block/index.js
Outdated
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.
We should always use footer for the citation IMO. Also, why don't we use html instead of text. The citation could include links, formatting etc...
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.
Just did it for this go around, so the output looked nicer, when the actual component is finalized it should definitely be grabbing the html.
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.
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.
Good point. Let's do cite for everything!
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.
There's a difference. footer is a block element, which should be the wrapper of cite (if any). cite can also be used elsewhere, like in figcaption.
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.
Here's some info: https://w3c.github.io/html/grouping-content.html#the-blockquote-element
There are lots of possibilities to format this.
editor/blocks/quote-block/index.js
Outdated
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.
We're not using it yet, but it's the responsibility of the save function to return the correct markup to be stored in postContent. Typically, this should return <blockquote><p>content</p><footer>citation</footer></blockquote>
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 figured, I was waiting on feedback before going ahead and changing this.
the responsibility of the save function to return the correct markup to be stored in postContent
Should it use renderToString(element) instead as a return value? Or what is the purpose of returning the react element itself?
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 think the current idea is to use renderToString outside the block (in a global maner). Relevant to this discussion #284 (review)
#302 (comment)
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.
Sounds good, so save() will be a way to create a clone of the element basically?
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.
Yep, A clone of the output element (could be different than the edit element shown on the editor). I personally still have a preference for return a string instead of an element, but I'm ok trying this way and see how it feels, we can still reconsider later.
|
Rebased and changed |
0978081 to
7817999
Compare
|
Tried rebasing and force pushing. The fork looks fine but for some reason the PR still thinks two files that were removed still exist which is causing the test suite to fail. Any ideas on how to fix? Update: I made an error in my code, that is the reason for the tests to fail; oooops. |
|
This seems to be working pretty well now. |
editor/blocks/quote/index.js
Outdated
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.
The category should be common. see #323 (comment)
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.
woops.
editor/blocks/quote/index.js
Outdated
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.
Shouldn't this follow the same markup we use for the save?
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.
Probably, the only problem is that this will not work for our example post-content.js if we change it to just cite. We will want to make a decision on the markup.
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 not against changing the post-content too. Anyway, this is not blocking for me right now
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.
Do you want to do footer or cite for now?
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.
Going to do footer for now.
editor/blocks/quote/index.js
Outdated
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 think, this should be onChange( { value: newValue } )
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.
That sounds right.
editor/blocks/quote/index.js
Outdated
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 think, this should be onChange( { citation: newValue } )
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.
Yup, good catches all around 👍
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 think it is { value: newValue } again? value is a prop for the Editable component, where as citation is a prop for the quote component. onChange() is feeding the Editable component.
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.
the onChange is a callback we need to call to update the block attributes parsed originally by the attributes object above. This second Editable is responsible of editing the citation block attribute, so I think we should call onChange( { citation: newValue } )
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.
Okay, I think I understand what you are saying. I'm still not 100% clear on what the role of onChange() is.
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.
Let me try to explain:
On the parent component rendering the blocks, we keep track of the current state (block list) and each time this state is updated we re-render the blocks by calling the edit method of each block.
When this parent component renders the block (calling edit) it provides an onChange callback, the block can call to update its state (its attributes), so anytime a block attribute needs to be changed, we should call onChange({ [ attributeName ]: attributeValue })
calling the onChange will update the state and triggers rendering the blocks again. This is how the unidirectional data flow of React (and similar) works.
I hope this is clear enough
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 think I understand that. I am having trouble on figuring out where onChange() hooks up to in the code. And where the onChange is being passed in.
I see :
{ blocks.map( ( block, index ) =>
<div key={ index }>
{ wp.blocks.getBlockSettings( block.blockType ).edit( block.attributes ) }
</div>
) }I do not see where the onChange is passed in, in the code currently. Or is there some magic going on somewhere?
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.
Oh I think you need to rebase :)
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.
Looks like it.
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.
Okay it makes a lot more sense now.
editor/blocks/quote/index.js
Outdated
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 we could write html( 'blockquote p' ) to avoid the concat in the edit method
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.
Sounds good!
Here is an initial attempt at using the current API for a quote block. I just guessed as to how this should be structured, honestly have no idea but I tried to mimic the codebase the best I could.
e351ec7 to
89c8dce
Compare
|
I updated everything. Let me know what you think. |
youknowriad
left a comment
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.
We'll probably need some styling to match the prototype UI, but this is good for now. 🚢 it
|
Okay, I'll add the styles real quick. |
|
@BE-Webdesign The text block lacks styling too, so If you want to keep this for later (maybe together with the text block, it's ok too) |
|
Too late. I added it already and rebased the latest commits into one. |
| @@ -0,0 +1,24 @@ | |||
| #editor blockquote { | |||
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 think it's preferable to add a class to the quote block (Similar to this https://github.com/WordPress/gutenberg/blob/master/docs/tinymce-per-block/src/blocks/quote-block/form.js#L95). Other blocks may use the blockquote with other styling options.
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.
Okay, sounds good. core-quote?
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 not the best person for naming things hahaha :) Always had trouble with it. I'll choose editor-blocks-quote to match the folder structure, I guess we should define a convention somewhere
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 think matching the slug for the block would be a good place to start. That way the css classes are namespaced like the blocks are.
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.
At some point we'll also need to consider if blocks can define their own default styles when rendered on the front-end of a site.
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.
At some point we'll also need to consider if blocks can define their own default styles when rendered on the front-end of a site.
To make it overridable, the fewer inline styles the better. The editor can't prevent the user from adding inline styles in raw mode, but it shouldn't encourage them in visual mode. The theme and plugins will be supplying the styles for the front-end look, though, so the block itself shouldn't have "default" styles beyond what the browser defines. The current method of defining editor-style.css should work the same way for this, right?
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.
Right, I don't think we'd want to encourage any inline styles, which furthers the need to consider stylesheets distinguished between the editor and front-end versions of a block. While the blocks here are first-party, we should outline and use an approach consistent with what we expect from plugin authors who implement their own blocks (a plugin which implements a slider block will need ability to define editor and front-end baseline styles). Whatever we land on, I agree that theme styles should always take precedence when defined for a block.
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.
To be clear, I think the editor should not define any styles for rendering the content in the blocks.
The theme and plugins can supply styles, because they can also put those on the front end. But the editor can't put any on the real front end, so to get the correct preview, it shouldn't put any in the editor either.
As for "blocks defining their own default styles", a block is either core or added by user code, so how is it "defining its own"?
| @@ -0,0 +1,24 @@ | |||
| #editor blockquote { | |||
| margin: 2em; | |||
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.
margin: 0 ?
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 was just copying styles that I found in one of the prototypes. I can change these later got to go for now.
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.
Yep, I guess the other one had a margin in all the blocks. The margin felt a bit weird while testing.
| category: 'common', | ||
|
|
||
| attributes: { | ||
| value: html( 'blockquote > p' ), |
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.
A blockquote could contain multiple paragraphs. In my explorer demo I use text: query( 'p', html() ) instead which assigns text as an array of each paragraph's HTML.
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.
Oh my bad, it was my recommendation
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.
Could we join the output directly using hpq?
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.
We will probably want to map the values in value to its of <p> component as well right?
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.
Could we join the output directly using hpq?
Not super cleanly, but possible. Since each matcher is just a function receiving a node, you can create your own pass-through:
attributes: {
value: ( node ) => query( 'p', html() )( node ).join()
}Or some compose-y equivalent.
I don't know that it's necessary a big issue to need to deal with this in the render behavior though.
| return ( | ||
| <blockquote> | ||
| { value } | ||
| <footer> |
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.
We may want to account for the case that citation is empty and avoid including the footer if possible. This was one of the reasons for hpq: to account for cases where the markup isn't always necessarily the same in the generated output (maybe a quote citation, maybe an image caption, but not necessarily).
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.
Yup sounds good! For some reason this slipped my brain, great catch. Does hpq return an empty value if there is nothing matched by the query?
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.
Does
hpqreturn an empty value if there is nothing matched by the query?
Yeah, specifically an undefined value if I recall correctly.
|
As of #335, a block's edit: class extends wp.element.Component {
componentDidMount() {
fetch( '/resource' ).then( /* ... */ );
}
render() {
const { attributes } = this.props;
// ...
}
}Not sure yet what naming conventions we'll want to recommend for the argument passed to the function (for ES5 implementations; ES6 can easily destructure). Maybe Just now realizing the docs example needs to be updated. |
|
Closing in favor of #419. |
Here is an initial attempt at using the current API for a quote block.
I just guessed as to how this should be structured, honestly have no
idea but I tried to mimic the codebase the best I could.