WordPress / gutenberg

The Block Editor project for WordPress and beyond. Plugin is available from the official repository.
https://wordpress.org/gutenberg/
Other
10.3k stars 4.11k forks source link

Scripts defined in the editorScript property in block.json are loaded outside the iframed editor #59528

Open mrleemon opened 6 months ago

mrleemon commented 6 months ago

Description

Some old scripts, such as the vanilla JS version of Masonry included in WordPress, need to be loaded inside the iframe to function correctly in the new iframed editor. According to this post: [link to the post], one can use defaultView to access the script.

While developing a custom block that requires the Masonry library, I've observed that specifying the scripts using the editorScript property does not load them INSIDE the iframe.

"editorScript": [
    "imagesloaded",
    "masonry",
    "file:./index.js"
]

However, using the script property to specify the Masonry and ImagesLoaded libraries does load them INSIDE the iframe. Nonetheless, the Masonry layout is not functioning as expected because the main block script is still loaded OUTSIDE of it.

"script": [
    "imagesloaded",
    "masonry"
],
"editorScript": "file:./index.js"

Is this behavior by design? If so, why? Or could it be a bug?

Step-by-step reproduction instructions

  1. Create a block with a block.json file that loads Masonry and ImagesLoaded using the editorScript property:
"editorScript": [
    "imagesloaded",
    "masonry",
    "file:./index.js"
]
  1. Add this new custom block to a template in the Site Editor
  2. Check the source code of the page and see that the scripts are loaded OUTSIDE the iframed editor

Screenshots, screen recording, code snippet

No response

Environment info

WordPress 6.4.3 No Gutenberg plugin

Please confirm that you have searched existing issues in the repo.

Yes

Please confirm that you have tested with all plugins deactivated except Gutenberg.

No

t-hamano commented 6 months ago

Hi @mrleemon,

Is this behavior by design?

I think this is the intended behavior. If the library can target and initialize an iframe, you should be able to properly use the script via useRefEffect. Please refer to the post below.

Blocks in an iframed (template) editor – Make WordPress Core

mrleemon commented 6 months ago

Yes, I know that post. I linked to it in my original question. My question was why placing the scripts in the script property loads them INSIDE the iframe, whereas putting them in the editorScript does not. It seems illogical to me.

stokesman commented 6 months ago

editorScript has always put the scripts in the root window (before there ever was an iframed editor) and it has probably been kept that way to avoid potential breakage. Changing where editorScript puts the script tags could likely be done but I suppose it’d require a new API or extending the current API for a block to opt in to and someone would have to prove a use case where it is necessary.

the Masonry layout is not functioning as expected because the main block script is still loaded OUTSIDE of it

I've been able to make Masonry layout work as expected despite this. It is less straightforward than it would be if Masonry didn't have its inherent cross-frame limitations (in which case it could be loaded by editorScript).

mrleemon commented 5 months ago

In this example test:

https://github.com/WordPress/gutenberg/blob/c7ff22ae513ecc0cdc0bda9c9fffedd53b2aee9b/packages/e2e-tests/plugins/iframed-masonry-block/block.json https://github.com/WordPress/gutenberg/blob/c7ff22ae513ecc0cdc0bda9c9fffedd53b2aee9b/packages/e2e-tests/plugins/iframed-masonry-block.php

rather than directly referencing a script file, a script handle is used in the editorScript prop. This makes the block script (editor.js) load inside the iframe. Why is the behavior different when using a script file directly?

stokesman commented 5 months ago

This makes the block script (editor.js) load inside the iframe.

I tested and didn’t find that to be the case. The editorScript is still in the root document.

That test plugin is kind of a funny example. When the editor is iframed, the block’s editorScript doesn’t create a Masonry instance on first render (because Masonry isn’t yet defined). It’s the script that runs in the iframe whose Masonry instance effects the elements in the iframe. If it weren’t for that, the block’s edit component would have to re-render before the layout takes effect. When it does re-render after Masonry is defined in the iframe then its Masonry instance is created and also applies (and that seems potentially problematic outside of a testing scenario). On the flip side—when not iframed—the script happens to execute too early to find the elements it would effect and the Masonry instance created by the block’s edit function handles them.

As for why the example’s block.json specifies script handles instead of files, I'd guess it was for easier/neater maintenance. When referencing files, a [filename].asset.php has to exist to return dependencies/version for each. That would add two more files for this test plugin.

onetrev commented 3 months ago

I've also done a fair bit of testing and I agree with @stokesman that using a script handle still meant the editorScript is outside of the iframe. However, doing this did make one big change that confused me and make me think it was in the iframe. When you use a script handle with editorScript then instead of it being loaded in the header it switches to be in the footer. So for me, and I don't know why this is, some things started to work. For example I'm using ACF Blocks and this if statement resulted in true.

if ( window.acf ) {
  window.acf.addAction( 'render_block_preview/type=acf/testimonials', initializeBlock )
}

Either way, I agree with @mrleemon that it's odd (and also unclear in the docs) that this is how things will work. And there definitely is a need for editorScripts in the iframe in my opinion. My use case is a slider blockj. I don't want the same script settings in the admin (ex: scrolling every 3 seconds) as I have on the front-end. So I should able to use a viewScript and editorScript to get the job done, but can't without the extra steps of using a script-handle -- but then the wp-scripts build process no longer creates a build file (since it's no longer in block.json). So now it's a double pain. :)