-
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
Reusable Blocks: Support importing and exporting reusable blocks #9788
Conversation
reader.onload = function() { | ||
resolve( reader.result ); | ||
}; | ||
reader.readAsText( file ); |
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 have to check browser support for this one to see if we need a polyfill.
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 have to check browser support for this one to see if we need a polyfill.
Did you check? As best I can tell from browser support resources, it should be supported.
https://developer.mozilla.org/en-US/docs/Web/API/FileReader/readAsText#Browser_compatibility
This is amazing. If you were more on fire, you'd cause nuclear fusion and solve the world energy crisis. Totally love it. Thanks so much for working on this. A few screenshots.
But overall, this is SO SO SO VERY SOLID. It works remarkably well, and I really dig how you've gone for the simplest possible solution here. Along with the multi-selection reusable blocks PR, this is going to be magical. It's also so magical that my excitement is getting the better of me. So please categorize my feedback into two categories, soon and future. Things we should do soon, if not in this PR: 2, 3, 4 are all fine. 5 obviously needs to be fixed. 1 is the weakest part, simply because it's so hard to find out where to manage your reusable blocks. I think it's probably fine to have the cog in the reusable blocks panel, but the feature is becoming so cool I feel it needs better placement. The first place I would look for this is in the More menu. We could have an item there that simply says "Manage reusable blocks". Or, we could have multiple items there — Manage Reusable Blocks, Import Blocks, Export Blocks. Some of these items also depend on where we go with #9732. For example I think it would be neat to default to importing blocks that are immediately editable, i.e. they are not reusable. And then repositioning "reusable" to be an addition on top of that. They could even be renamed Templates (immediately editable blocks) and Global Templates (new name for reusable blocks). In such a situation perhaps the page could be "Manage Block Templates". Things we should think about and maybe do afterwards, or in the future:
Okay I'm going to splash some cold water on my face. Immediate next step to me seems like it should be to copy the "Manage Reusable Blocks" link to the More menu in the to right corner. With that in place (and 5 fixed), I think this could actually be good to go. |
This is a great addition!
I agree that making this feature more visible is a good idea.
Speaking of using reusable blocks as templates: #8403. I recommend checking out how Beaver Builder, Divi, and Oxygen implement this kind of thing for some inspiration.
Yes to all of this!
Sounds good to me! |
0112cb4
to
41a2ac5
Compare
|
What are testing instructions here? Trying to infer from code, when navigating to
|
file: null, | ||
}; | ||
|
||
this.isMounted = true; |
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.
Re: Previous comment, this is actually a part of the React component API, though discouraged, which may explain errors around attempts to set it.
https://reactjs.org/blog/2015/12/16/ismounted-antipattern.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.
I know the antipattern but sometimes I'm pragmatic and don't want to introduce more complex solutions like a cancelable promise or something like that. Especially since it's a small JS file for a separate page 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.
Also, noting that I wasn't able to reproduce any of the errors you had, so I'd appreciate another check
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 point was less about it being discouraged / deprecated, and more to the point that this is already a property defined on a default React component, and we're overriding it.
} ); | ||
|
||
// Setup Import Form | ||
document.addEventListener( 'DOMContentLoaded', function() { |
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.
Inconsistency: function() {
(function keyword) here vs. ( event ) => {
(arrow function) above.
const showNotice = () => { | ||
const notice = document.createElement( 'div' ); | ||
notice.className = 'notice notice-success is-dismissible'; | ||
notice.innerHTML = `<p>${ __( 'Reusable block imported with success!' ) }</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.
Grammar: "successfully" might work better than "with success" here.
} | ||
|
||
const showNotice = () => { | ||
const notice = document.createElement( '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.
Could we render a <Notice />
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.
I did try it, the issue is that it's styling is very different from the regular notices in this kind of pages. We should reconsider if we rewrite the whole page in JS
const postType = await apiFetch( { path: `/wp/v2/types/wp_block` } ); | ||
const reusableBlock = await apiFetch( { path: `/wp/v2/${ postType.rest_base }/${ id }` } ); | ||
const fileContent = JSON.stringify( { | ||
__file: 'wp_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.
I'm trying to understand what this is for. Is this a marker of the file being of block format? Does it really matter? Or could we just infer by validity of the rest of the file (being JSON, having title, etc).
Or at least, "file" may not be a well-describing word here (vs. "format", "type", etc).
Also wondering if there's some prior art here to lean on without over-complicating (JSON-LD, JSON Schema, xmlns).
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.
yes, this is just a marker. My idea is that this will remove any ambiguity while trying to auto-import this file (like if we implement drag and drop to the editor). We could detect that the imported file has a "title" and "content" strings (JSON Schema) but this seems too common, I prefer as always explicitness over implicitness. What if we add another exported file with the same format that should imported differently? (like in a File block or something).
I also considered adding a version
field if ever we change the format but I decided against because we can think of a missing version as version: 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.
Another thought: Do we ever intend to support batch import / export? Would this file contain multiple at some point? Will this format support that as a future enhancement?
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.
Explicitness is fine, just seems a format arbitrarily decided upon.
try { | ||
parsedContent = JSON.parse( fileContent ); | ||
} catch ( e ) { | ||
throw new Error( __( 'Invalid JSON file' ) ); |
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 an error message be localized? Maybe what we present in the UI, but it doesn't seem like we're barred from translating the language-neutral error.message
to a localized form.
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 is present in the UI, that's why I did it. We do something similar in media-upload
. I can add a code and a message maybe, that way the code is constant?
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.
Actually, we can't attach a code to the Error
object, maybe it's fine 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.
The usage in UI should be secondary to having consistent, predictable messages for errors. One place I've seen this become an issue is in error aggregators (think Sentry, Rollbar), where you'll have duplicates of the "same" (but localized variant of) errors.
I'd even consider something like:
let uiMessage;
switch ( error.message ) {
case 'Invalid JSON file':
uiMessage = __( 'Invalid JSON file' );
break;
}
{ | ||
"name": "@wordpress/list-reusable-blocks", | ||
"version": "1.0.0", | ||
"description": "Adding Export/Import support to the reusable blocks listing.", |
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.
Really stress-testing our policy of publishing all the things as modules, huh 😅
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 made it private :)
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.
Now that I think about it. Making it private means it can't be included in Core as an external package and begs the question of whether it should be moved to Core or kept in the repo post-merge.
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, I never said I was opposed to it being published to npm 😉
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.
yes, I know it was private even before the comment :)
904f010
to
34d4b4d
Compare
$actions['export'] = sprintf( | ||
'<a class="wp-list-reusable-blocks__export" href="#" data-id="%s" aria-label="%s">%s</a>', | ||
$post->ID, | ||
__( 'Export as JSON', 'gutenberg' ), |
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.
Is this convention lifted from somewhere? Doesn't seem obvious why we'd need an aria-label
which has the same text as the 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.
yes, it's inspired by the way we add the "Classic editor" link in the post list actions
const postType = await apiFetch( { path: `/wp/v2/types/wp_block` } ); | ||
const reusableBlock = await apiFetch( { path: `/wp/v2/${ postType.rest_base }/${ id }` } ); | ||
const fileContent = JSON.stringify( { | ||
__file: 'wp_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.
Another thought: Do we ever intend to support batch import / export? Would this file contain multiple at some point? Will this format support that as a future enhancement?
reader.onload = function() { | ||
resolve( reader.result ); | ||
}; | ||
reader.readAsText( file ); |
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 have to check browser support for this one to see if we need a polyfill.
Did you check? As best I can tell from browser support resources, it should be supported.
https://developer.mozilla.org/en-US/docs/Web/API/FileReader/readAsText#Browser_compatibility
const postType = await apiFetch( { path: `/wp/v2/types/wp_block` } ); | ||
const reusableBlock = await apiFetch( { path: `/wp/v2/${ postType.rest_base }/${ id }` } ); | ||
const fileContent = JSON.stringify( { | ||
__file: 'wp_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.
Explicitness is fine, just seems a format arbitrarily decided upon.
uiMessage = __( 'Invalid Reusable Block JSON file' ); | ||
break; | ||
default: | ||
uiMessage = __( 'Unknow error' ); |
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.
Typo: "Unknow" -> "Unknown"
A good puzzle to solve is going to be handling local resources (IDs, etc) through this process. |
Some usability issues with editing reusable blocks noted at #9964, made more apparent here with the introduction of the "Manage All Reusable Blocks" link. |
Was able to export and import reusable blocks with no issues. Just one thing which is a bit inconvenient is you'd have to refresh the page after importing to show the imported block. |
@janicecchua yes, that's true and that's a compromise we made for now. I talked about it above
|
This PR adds Export/Import capabilities to the page listing the available reusable blocks.
Remaining