-
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
Plugin: Quote Block Initial Attempt #327
Conversation
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
@@ -7,6 +7,7 @@ import * as query from 'hpq'; | |||
|
|||
export { query }; | |||
export { default as Editable } from './components/editable'; | |||
export { default as EditableQuote } from './components/wp-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 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
|
||
attributes: { | ||
value: query( 'blockquote', html( 'p' ) ), | ||
citation: query( 'cite,footer', text() ) |
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
}, | ||
|
||
save( attributes ) { | ||
return wp.element.createElement( 'p', null, attributes.value ); |
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
wp.blocks.registerBlock( 'core/quote', { | ||
title: 'Quote', | ||
icon: 'format-quote', | ||
category: '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.
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
|
||
attributes: { | ||
value: query( 'blockquote', html( 'p' ) ), | ||
citation: html( 'cite,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.
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
<blockquote> | ||
<Editable | ||
value={ combinedValue } | ||
onChange={ ( newValue ) => onChange( { 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.
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
<cite> | ||
<Editable | ||
value={ citation } | ||
onChange={ ( newValue ) => onChange( { 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.
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
category: 'quote', | ||
|
||
attributes: { | ||
value: query( 'blockquote', html( '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.
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. |
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
hpq
return 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.