-
Notifications
You must be signed in to change notification settings - Fork 4.6k
Experiment: Add content only block controls to Inspector Panel #71577
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
Conversation
…ocking Move contentOnly template locking code from selector to reducer Simplify getBlockEditingMode tests and add additional cases Remove unused dependencies WIP
- Pass paneId to tools panel items so that reset works. - set shownByDefault consistently.
| const resetValue = {}; | ||
|
|
||
| attributes.forEach( ( attribute ) => { | ||
| resetValue[ attribute.key ] = getDefaultValue( attribute ); |
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 reset options in the tools panel would be better if they reset to the pattern's original content (similar to how synced patterns work) rather than the attribute's default.
I haven't explored it yet, but I think it'll be pretty difficult to do. 😬
|
Size Change: +478 B (+0.02%) Total Size: 1.94 MB
ℹ️ View Unchanged
|
| "role": "content" | ||
| "role": "content", | ||
| "control": { | ||
| "type": "RichText", |
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.
Rather than specifying the type of control that the attribute needs, I wonder if we should be more semantic, and let Gutenberg decide what control to display for each different piece of semantic content, for example:
- Media
- Link
- Text
- 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.
I agree. I really think we should lean into semantics here. The more semantics we can have blocks give us, the more we can generate an appropriate UI. Then, we're also not locked into a certain approach, but yet we have enough information to infer the right 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 agree about the idea of more semantic names. The upstream issue specifies that formatting should be possible so that's why I went with RichText vs. Text (which might represent Plain Text).
It could also just be Text and inferred as rich text from the attribute.type.
Something else to consider is that Media updates the id, src and potentially others like caption and alt, so the idea of one control per attribute may not work. Or it does work, but we need to allow a control to read and write to other attributes, not just its own.
|
Flaky tests detected in 04a67c2. 🔍 Workflow run URL: https://github.com/WordPress/gutenberg/actions/runs/17609960850
|
getdave
left a 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.
This is great. Thank for working on it.
I also started exploring in #71567, but mine was just a quick mockup and yours seems a lot more fleshed out.
I'm wondering if the API for this could be applied in the following priority:
- use
controldefined inindex.js(if available) - use
controlfrom Block JSON to render predefined control (if available - as shown in this PR) - attempt to automatically infer control by
type
This would allow the block author to opt-in to ever greater levels of fidelity for their controls.
| "control": { | ||
| "type": "RichText", | ||
| "label": "Content", | ||
| "shownByDefault": 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.
I wonder if we can infer a suitable field based on the type of the attribute? I did a very basic MVP for that in #71567.
I definately think your plan to be able to manually specify or override the control component is required though.
A scenario I've been thinking about is attributes such as url. This would typically map to a <LinkControl> but that component needs to know the type of entity being searched for. So ultimately we to be able to either:
- provide this data via the block json
- configure it manually via JSX
I saw you suggested using index.js for this which I think might be a good opt-in API for these kinds of edge cases.
jeryj
left a 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.
I've been letting this percolate in my brain a bit. I think we should do all we can to have this be inferred controls based on the existing block.json. I think if we require all third party blocks to be updated to have write mode work, then write mode fails.
Example:
- We launch write mode
- Most sites use third party blocks
- User switches on write mode
- Only core blocks work/all their third party blocks are broken
If we can infer the controls, the write mode will automatically work for third party blocks.
|
|
||
| const blockTitle = useBlockDisplayTitle( { | ||
| clientId, | ||
| context: 'list-view', |
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 feels odd since we're not in the list view. Maybe we need to change the whatever the context is of this list-view name to something more generic?
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 haven't thought too much about it, it's just code I lifted from the BlockQuickNavigation component.
| "control": { | ||
| "type": "RichText", | ||
| "label": "Content", | ||
| "shownByDefault": 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.
I think this works for now to get design feedback, but I'm not sure we should rely on the idea of having all block authors updating their blocks to get their block to work with write mode if we can avoid it.
However, they will need to make updates to their block code if they want any block tools to show, so maybe adding to the block.json is the simplest way to define this.
| "role": "content" | ||
| "role": "content", | ||
| "control": { | ||
| "type": "RichText", |
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 agree. I really think we should lean into semantics here. The more semantics we can have blocks give us, the more we can generate an appropriate UI. Then, we're also not locked into a certain approach, but yet we have enough information to infer the right controls.
|
How possible would it be to use a DataForm for this UI? |
| setValue, | ||
| value, | ||
| } ) { | ||
| const Control = getControlForAttribute( attributeDefinition ); |
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 logic is basically repeating what DataForm does, so it's probably wiser to just use 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.
Yeah, I think we should use DataForm. We probably need some way to show/hide controls from the UI a bit like ToolsPanel does, and some custom controls for some things, but I that work is beneficial.
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 patterns with a lot of blocks would be the same as a block with a lot of settings.
The potential is not just a lot of blocks, but also lots of content in the value field too.
What do folks think a minimal and realistic enhancement looks like? An accordian/hide show wrapper around Data form fields for example?
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 inevitability is the request that clicking on a block on the canvas focusses or opens the editable fields. Take this contrived example:
Kapture.2025-09-11.at.14.40.10.mp4
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'd like to try merging all those separate Paragraph items into one rich text field that then builds text blocks. Basically a markdown-like textfield that will generate paragraph and list blocks off of it, so it's treated as one "content box."
Again, very very experimental and no idea if it will work. But, I imagine it like:
- if there is a paragraph or list block
- create a content area in the sidebar
- check for siblings to find the content boundary (wherever the paragraph or list blocks end)
- translate these paragraph and lists into simple text (paragraph, line break, paragraph, line break, list, line break, etc) in the content area
- When changes are made to the sidebar content area, they get updated on the canvas as blocks
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'd like to try merging all those separate Paragraph items into one rich text field that then builds text blocks.
Interesting. I'm very curious to see how you'd approach 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.
When exploring this, I think we'll need to take into account RTL and vertical languages as well (#71517 (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.
Another inevitability is the request that clicking on a block on the canvas focusses or opens the editable fields. Take this contrived example:
This is on my mind too. Perhaps we can soft-select the matching block while editing in the sidebar (show the outline but not focus).
Maybe we can scroll to or show some kind of selection indication in the inspector when a block is selected in the canvas.
Unfortunately it will be necessary for third party blocks to update anyway, because blocks are only editable in write mode if they have I had a go a testing a bunch of third party blocks recently, and the only ones that worked in write mode were ones using core blocks inside (like, a block that wraps around a Paragraph and a Heading or something). I don't believe there's a viable way of inferring that a block is content without the explicit role. There are just too many different types of blocks for that to work out without ending up in spaghetti logic and likely a bunch of hard-coded edge case blocks 😬
The good news here is that given the direction set in #71517, it's looking likely that there won't be a write mode switch, but the mode will be a default for patterns only. This may somewhat mitigate the issues with third party block compatibility, because anything that's not in a pattern with a |
I wonder how far we can get by still using the explicit role ( That way for third party blocks, they only need to add the content role and not worry about any other details.
|
I think block bindings does something like this, but that's a very advanced feature. It'll need to be internationalized - I don't know whether that's possible or how it'd work. Translators would probably need to work with a camel cased version of the attribute key. I think some of the existing keys are also not quite right as labels, some like I think we need proper labels. The investment will, I expect, be worth it. Block Bindings could use it too. I don't think it'll take long either, we just need to decide the API first. Could probably get an AI agent to do a first pass and then human refinements. It also doesn't have to be perfect first time, just good enough. I do like the idea of inferring the type of control, but I think realistically we'll hit issues soon enough, and we might want some level of curation of the controls. |
|
Great discussions,
I've only just begun to look at this PR and @getdave and @jeryj's PR, and have made some notes. I hope I'm grokking this all so far: With inference:
Manual config:
I'll revisit to test my assumptions, but so far I'm slightly leaning towards inference in the first iteration, or perhaps some hybrid model, e.g., try automatic inference first, then later, create a system to allow optional manual overrides in block.json or JS for blocks that require custom editing experiences or advanced data types. For the latter, @talldan already mentioned We have short-term goals but with long-term consequences, so I'm wondering if we should — at least in the first round — assume simple needs and avoid maintenance overhead, and go for what covers the most number of cases. After that, spend quality time on an API in index.js or otherwise to manually override the defaults? |
@jeryj I definitely get the concerns. At the same time, if the inferred controls are wrong then the third party blocks are probably more broken, users will be able to add incorrect block data and possibly invalidate the blocks or cause errors to be thrown. Even core blocks are very inconsistent in their implementation and I expect it to be a challenge, so I'm not sure we'll be able to solve every problem for third party blocks who might be using different practices (like different naming conventions). I'm not saying there's no merit to the idea, but I think we might need to be careful and get the balance right. |
For sure! That's a huge concern of mine too. I want to know where it beaks down so we can make the best decision we can for now. Maybe there's a way we can do both - automatically infer and offer a way to override with your own control. |
|
Closing this as I don't want to duplicate efforts - #71567 is where dev will happen now. |
What?
Experiments with an API for #71557.
Built on top of #71512
Related: #58233.
Similar: #60779.
The goal is to make
role: contentattributes editable in the block inspector in contentOnly mode, as per this design:This PR starts with the
contentfields for heading and paragraph blocks.Why?
See #71517 for the full reasoning.
How?
For this PR I'm proposing a new API in the block.json, for each attribute a
controlproperty can be specified. At the moment onlyRichTextcontrol is supported, and to start with this renders aTextBoxcontrol, so not a rich text, but it's something to build on.The exact API might need refinement, for example, should it be in block.json or index.js? I like
block.jsonbecause it co-locates with the attribute.I expect blocks will also need custom controls for more specific things, in which case moving to the index.js might be better, or adding a way to register controls.
In the future this could expand beyond contentOnly and the inspector.
Testing Instructions
First, enable the experiment in Gutenberg's experiment settings page:
Screenshots or screencast
Kapture.2025-09-10.at.17.36.52.mp4