I've been looking for a way to force a block to only be used at the root level. I'm trying to create a block taxonomy where only one block type (A) is allowed in the root, and it only allows another block type (B) as its child, and then B allows a restricted set of blocks, but not A or B, for example:
A > B > !(A|B)
The context here is implementing AMP Stories in Gutenberg. The structure of an AMP Story is as follows:
So I have an amp-story
post type which should only take 1 or more amp-story-page
blocks, and the amp-story-page
blocks should only allow one or more amp-story-grid-layer
blocks, and then lastly amp-story-grid-layer
allows a subset of the core blocks (e.g. paragraph
, video
, image
) but not any amp-story-*
blocks.
At the moment, the amp-story-page
block is offered in the inserter to be added to the root and to any of the other nesting blocks because there is nothing constraining it otherwise.
I tried a top-down and bottom-up approach where I explicitly set the allowedBlocks
_and_ the parent
block property but thus resulted in some infinite recursion error.
I also tried to register a block with parent: []
but this just results in the inserter being hidden entirely, since all of the blocks other than amp-story-page
have a parent
set to be amp-story-page
or amp-story-grid-layer
.
Discussed in Slack at https://wordpress.slack.com/archives/C02QB2JS7/p1531158200000319
/cc @noisysocks
This definitely sounds like a use case we should support. I have a few ideas on how to enable it.
My first thought is that we should support this via parent
which is similar to what @mtias first suggested in https://github.com/WordPress/gutenberg/issues/5540.
registerBlockType( 'amp-story', {
parent: [ 'post', 'page' ],
} );
registerBlockType( 'amp-story-page', {
parent: [ 'amp-story' ],
} );
registerBlockType( 'amp-story-grid', {
parent: [ 'amp-story-page' ],
} );
We've gone back-and-forth on whether or not to include post types in parent
鈥攕ee discussion in https://github.com/WordPress/gutenberg/pull/6753. I think it's a clean API, though, and makes sense to allow a block to only be inserted into certain post types, just as we allow a block to only be inserted into certain block types.
This came up in https://github.com/WordPress/gutenberg/issues/5540 and https://github.com/WordPress/gutenberg/pull/6753 as well. The basic idea is to allow you to specify a blacklist of blocks that cannot go inside a <InnerBlocks>
, rather than just the whitelist which is what allowedBlocks
currently acts as.
registerBlockType( 'amp-story', {
edit() {
return <InnerBlocks disallowedBlocks={ [ 'amp-story' ] } />;
},
} );
registerBlockType( 'amp-story-page', {
parent: [ 'amp-story' ],
edit() {
return <InnerBlocks disallowedBlocks={ [ 'amp-story' ] } />;
},
} );
registerBlockType( 'amp-story-grid', {
parent: [ 'amp-story-page' ],
edit() {
return <InnerBlocks disallowedBlocks={ [ 'amp-story' ] } />;
},
} );
It's not clear what would happen if one specified both allowedBlocks
and disallowedBlocks
. We'd likely have to raise an error.
We can, alternatively, combine allowedBlocks
and disallowedBlocks
, which I experimented with in https://github.com/WordPress/gutenberg/pull/6753, e.g. allowedBlocks={ { 'amp-story': false, '*': true } }
would allow all blocks except for amp-story
. I sensed that this is too complex of an API, though.
Thoughts? cc. @WordPress/gutenberg-core
The use of parent
in option 1 certainly seems simpler. For option 2 using disallowedBlocks
it seems not-ideal to have to copy the root block for every nested block type.
I'm looking for something similar. I want to restrict the insertion of all block but one at the root level. So you can only insert one type of block at the root level. The rest can be inserted inside this block.
+1
The disallowedBlocks
option for <InnerBlocks>
is enough to doesn't let users repeat the same block inside itself, causing some visual error, sometimes difficult to see and revert. I'm thinking about a section block to wrap contents as an example.
+1
I experince most clients insisting to the classical editor and those few changing to GB tend to screw up their own websites because they are overwhelmed and even the UX is not intuitive enough.
I like the idea of disallowedBlocks for a beginning, however we build websites based on graphical layouts.
Those layouts have blocks but those blocks do not have content types (as they are all just graphical) but the designers give them aliases (like team member that contains a thumbnail and text for example).
What if later we could give our blocks such aliases and setup restriction rules based on those aliases rather than rules based on block types?
I mean most probably i do not want to make such a restriction that the client may not place an image block inside a text block... but for sure i do not want to allow them to place any text block in a block that has the alias "portrait gallery".
This way we would have an abstraction layer allowing us to group the very same block types together -but as they may have different aliases- we could set up different rules what content they are intended to contain based on the layout we got from the designer.
I couldn't make disallowedBlocks
works with me. Is it still active and implemented? Or removed from the current version of Gutenberg?
Also, this one couldn't work with me:
registerBlockType( 'amp-story', {
parent: [ 'post', 'page' ],
} );
I would like section
block I made just visible on root of the blocks...any ideas?
If it helps, here is another use case: custom "slides" post type with "slide" blocks. No other block should be inserted at the root.
@swissspidy Is this the issue we discussed at WCEU? :)
@ellatrix Yes I believe so!
Having a similar need to what a few have described: Limit root-level blocks to one type. Allow multiple blocks within that block.
The post type template_locks or allowed_block_types filter (root level) override the individual block templateLock and allowedBlocks.
Is it a possibility that block level templateLock and allowedBlocks can override these root-level settings (for their level), or does it need to stay that way by design? Considering the example, child block settings would override parent blocks for their level, and the next child would for theirs, and so on. It seems like there is already a WP precedence for children to override parents.
There's many use cases where users should have the flexibility of multiple blocks, but certain layout parts/levels are locked/limited (event/product/realty listings).
I face the same problem. For now I just hide unnecesary blocks depending where inserter is in DOM structure, but it's neither clean nor working 100%. If only we could set 'root' blocks. Have anyone managed to solve the problem?
+1
We need this as well, but with the added constraint of only allowing 2 blocks for a specific page template in the root.
For example, we have a landing-page template and a home-page template, and on those two templates, we need the only option for editors to be inserting a section block in the root. That section block in turn would allow for about 10 or so blocks in itself.
Would be super helpful for us since Gutenberg is inherently a little confusing to a lot of our editors (most of them aren't WordPress editors by trade, its just part of their job to update their sites) and even more confusing is that there are different rules for different page templates (sections can go in these two templates but not these. Your page looks bad here because you picked the landing-page template but didn't use a section...etc.) Really would save our support team a lot of hours of explaining that over and over again if we could just automate which blocks are available given certain conditions.
+1
The need for block restriction is very real.
Is it possible to de-couple the definition modules made in allowedBlocks prop (js) and the 'allowed_block_types' filter (php) so that the InnerBlocks module can grant provision of the allowedBlocks independently? This would provide a way of explicity defining sets of allowed blocks in both the Root and the InnerBlock.
The next step here is for a developer who's interested in helping out to explore one of the approaches (probably (1)) described in https://github.com/WordPress/gutenberg/issues/7845#issuecomment-404019138. I've added the Needs Dev
label to signify this.
Hello, I have registered a custom block as parent to all the default Gutenberg blocks. Also, inside the <InnerBlocks>
, I have specified the allowedBlocks attribute to all the default Gutenberg blocks since the parent block was displaying in the inserter of InnerBlocks. Now the problem I am facing is, when click on the plus icon of the column block, it directly appends the custom block without opening the inserter.
To solve this, I added the column block also as the parent for all the default blocks so the inserter opens when clicked on the plus icon of the column block. This created another issue that is the custom block is now visible inside the inserter which opened on click of the plus icon of column block.
My question:
My code
`
const parent = ['pykih/byo', 'core/column', 'core/group', 'core/media-text'];
addFilter('blocks.registerBlockType', 'assignParentBlock', (pSettings, pName) => {
return Object.assign({}, pSettings, {
parent: parent
})
});
`
It would seem to me that limiting by post type on parent could be limiting. Just specifying parent: 'root'
aught to be enough to limit the block to only be posted in a top level.
I would also suggest this as a "Why not both" scenario. It makes sense to add dissallowedBlocks
to the InnerBlock
api, but I don't see how that would solve the need to limit blocks to the page/post root.
+1 to dissallowedBlocks
so... still no way to prevent editors to add blocks in root level, besides the ones we supply in the template?
allowedBlocks={ ... }
Can we make the definition of parent
such that "no parent" is a valid test, and in the case above, if it is the only value then a block can only be used in the root? My use case is requiring a container for all WYSIWYG-like blocks since they are simple elements like <ul />
, but not for layout blocks which are already wrapped in div containers which can be used for positioning.
I think that adding "root" here is strange because root is not a "parent block", nor is "post" or "page" a parent block. Instead, there is "no" parent block. I think an empty array for parent
should also mean "no parents", as in it is only allowed in the root. But that could be a breaking change.
For the same reason, I disagree with adding "post type" to the parent definition. I think if we want to add post type restrictions to the block definition it should be as its own property postTypes
. I think it would be weird to express "allow on page
post type, with no parent or as a child of 'core/columns'" in a single array.
const Tabs = {
// only allow this block in root, don't need tabbed columns
parent: [], // or [ '' ]
}
const Accordion = {
// this can be in a root, a column, a tab
parent: ['', 'core/columns', 'myplugin/tabs'],
}
Another complementary idea which expresses the opposite of this would be to set the parent property to 'any'
or ['any']
(or core/any to prevent collisions with custom blocks?) -- allowing a block to be inserted into any InnerBlocks
, but NOT the root of Gutenberg. That way, the definition of parent
doesn't have to include every possible, constantly evolving set of blocks that can be parents. The default for this property would now be equivalent to parent: [ '', 'any' ]
.
If we can agree this is a good way forward, I'll try and take a look and open a PR.
Here's a workaround that could work:
Not the cleanest approach to nest the whole content in another div to mimic the root, but could work at least.
I didn't test this yet, but decided to write this idea down, since perhaps it could be useful for someone looking for a quick hack that makes this feature working now.
For now I just hide unnecesary blocks depending where inserter is in DOM structure,
@krywa5 can you show an example of how you do it?
I am looking for a way to do it but I have no clever idea
Most helpful comment
If it helps, here is another use case: custom "slides" post type with "slide" blocks. No other block should be inserted at the root.