-
Notifications
You must be signed in to change notification settings - Fork 4.3k
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
Convert a Classic Block into Multiple Blocks #3179
Conversation
return ( | ||
<IconButton | ||
className="editor-block-settings-menu__control" | ||
onClick={ () => convertToBlocks( 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.
Not entirely sure what the best way is here to avoid passing arguments.
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 desire to avoid passing the argument? If you mean so that the component doesn't need to be aware of a block
prop, the only way would probably be overriding react-redux connect
's third argument mergeProps
for the dispatch to be aware of the block from state, but... probably more work than is really worthwhile.
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.
Can you make the component not know about the block? Don't you have to return null
if the block isn't defined (or has an invalid name)?
ba9ad2b
to
eb371f2
Compare
( dispatch, { uid } ) => ( { | ||
convertToBlocks( block ) { | ||
dispatch( replaceBlocks( [ uid ], rawHandler( { | ||
HTML: block.attributes.content, |
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.
Although we also set it directly in the paste module, we might want to register this as well with the API. See
gutenberg/blocks/api/paste/index.js
Lines 109 to 111 in 1b444cb
return createBlock( getUnknownTypeHandlerName(), { | |
content: node.outerHTML, | |
} ); |
Atm we require the attribute to be content
implicitly.
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 call serializeBlock
here instead?
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. I guess we could. :)
@@ -50,6 +51,7 @@ function BlockSettingsMenu( { uids, onSelect } ) { | |||
<div className="editor-block-settings-menu__content"> | |||
<BlockInspectorButton onClick={ onClose } /> | |||
{ count === 1 && <BlockModeToggle uid={ uids[ 0 ] } onToggle={ onClose } /> } | |||
{ count === 1 && <UnknownConverter uid={ uids[ 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.
Does it make sense to include this in BlockToolbar (for mobile controls)?
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 entirely sure what you mean.
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 block settings menu is hidden on mobile and these controls are shown in the block toolbar instead here lob/master/editor/block-toolbar/index.js#L143 Thinking we could do the same with this control.
blocks/api/index.js
Outdated
@@ -6,7 +6,7 @@ import * as source from './source'; | |||
export { source }; | |||
export { createBlock, switchToBlockType } from './factory'; | |||
export { default as parse, getSourcedAttributes } from './parser'; | |||
export { default as pasteHandler } from './paste'; | |||
export { default as rawHandler } from './paste'; |
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.
Should we rename the directory as well?
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, of course.
import { getBlock } from '../selectors'; | ||
import { replaceBlocks } from '../actions'; | ||
|
||
export function UnknownConverter( { block, convertToBlocks } ) { |
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 don't have any bright ideas, but I think we should call this differently :)
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 LegacyConverter or ClassicConverter.
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've named it this way because we're checking getUnknownTypeHandlerName
. :)
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 mean, this is an API thing, not a block thing. The component shouldn't depend on what block handles unknown pieces right?
This is working well for me, nice to see that the pasting strategy is proving very worthwhile. Also, when undo works properly it feels very effortless to convert and switch back if you didn't like the result. One other thing that might be nice to add is to trigger a success notice with some text like "Converted to 12 blocks" or something like that, which would convey the more significant nature of the operation. |
0c67b4c
to
2743b51
Compare
Where should this notification pop up? Not really sure if it's any use to the user though? I like showing as little as possible, it just works. :) |
We could also select the converted blocks... (Which kind of indicates something just happened with those blocks.) |
I love this so much. ❤️ |
@@ -77,6 +77,15 @@ describe( 'paste', () => { | |||
|
|||
equal( filtered, '<em>test</em>' ); | |||
} ); | |||
|
|||
it( 'should always return blocks', () => { | |||
const blocks = paste( { |
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.
Should it be renamed to rawHandler
, too?
( state, { uid } ) => ( { | ||
block: getBlock( state, uid ), | ||
} ), | ||
( dispatch, { uid } ) => ( { |
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.
You have block
already exposed in props
. The following should work:
( dispatch, { block, uid } ) => ( {
convertToBlocks() {
dispatch( replaceBlocks( uid, rawHandler( {
HTML: serialize( block ),
inline: false,
} ) ) );
},
} )
Although @aduth might be correct that you would have to use 3rd param mergeProps
here.
Yes, 2nd param is ownProps
...
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.
Another funky way to do the same is to wrap this with HOC withBlock
:P
const withBlock = component => connect( ( state, { uid } ) => ( {
block: getBlock( state, uid ),
} ) )( component );
export default connect(
null,
( dispatch, { block, uid } ) => ( {
convertToBlocks() {
dispatch( replaceBlocks( uid, rawHandler( {
HTML: serialize( block ),
inline: false,
} ) ) );
},
} )
)( withBlock( UnknownConverter ) );
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 fine with keeping as 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.
👍
2743b51
to
eaefd8a
Compare
editor/block-toolbar/index.js
Outdated
@@ -78,6 +79,7 @@ class BlockToolbar extends Component { | |||
<BlockInspectorButton small /> | |||
<BlockModeToggle uid={ block.uid } small /> | |||
<BlockDeleteButton uids={ [ block.uid ] } small /> | |||
<UnknownConverter uid={ uid } small /> |
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 seems like block.uid
should be passed as a prop instead.
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 agrees with me:
/Users/gziolo/PhpstormProjects/vagrant-local/www/wordpress-default/public_html/wp-content/plugins/editor/block-toolbar/index.js
82:32 error 'uid' is not defined no-undef
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.
Whoops
8c666d6
to
79f6c6a
Compare
Codecov Report
@@ Coverage Diff @@
## master #3179 +/- ##
==========================================
+ Coverage 31.11% 31.47% +0.36%
==========================================
Files 230 231 +1
Lines 6448 6544 +96
Branches 1150 1181 +31
==========================================
+ Hits 2006 2060 +54
- Misses 3726 3750 +24
- Partials 716 734 +18
Continue to review full report at Codecov.
|
editor/block-toolbar/index.js
Outdated
@@ -78,6 +79,7 @@ class BlockToolbar extends Component { | |||
<BlockInspectorButton small /> | |||
<BlockModeToggle uid={ block.uid } small /> | |||
<BlockDeleteButton uids={ [ block.uid ] } small /> | |||
<UnknownConverter uid={ block.uid } small /> |
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.
Can we put it before Delete button? This is how it looks at the moment:
@jasmussen @karmatosed, what would be the best place to put 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.
Sorry, yeah, should be the same as normal drop down. :)
79f6c6a
to
fecc4b3
Compare
I tested it with the following HTML code from the post created with the classic editor: It works perfectly fine with some exceptions: embed urls are converted into paragraphs (2 urls that were into their own lines were combined into one paragraph). I know it's out of scope here, but is there plan to improve that in the future? |
@gziolo Yeah, that's correct, URLs currently go through the text patterns... We should change this in another PR. |
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.
This works perfectly fine using the same API as we offer with paste functionality. Great job integrating this. Thanks for addressing all issues I raised when looking at this code 🙇
I left a comment about my existing post that would benefit from future improvements applied to rawHandler
, but it's rather nice to have.
I noticed some issues with Travis but I don't think it has anything with the changes introduced here. It would be nice to confirm before merging this PR. |
Description
See #3149.
This PR adds a button to the right side dropdown for freeform blocks to convert it into separate blocks.
How Has This Been Tested?
Thoughts: