-
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
InspectorControls: Wrap block support slots in ToolsPanel #34157
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
/** | ||
* WordPress dependencies | ||
*/ | ||
import { __experimentalToolsPanel as ToolsPanel } from '@wordpress/components'; | ||
import { useDispatch, useSelect } from '@wordpress/data'; | ||
|
||
/** | ||
* Internal dependencies | ||
*/ | ||
import { store as blockEditorStore } from '../../store'; | ||
import { cleanEmptyObject } from '../../hooks/utils'; | ||
|
||
export default function BlockSupportToolsPanel( { children, label, header } ) { | ||
const { clientId, attributes } = useSelect( ( select ) => { | ||
const { getBlockAttributes, getSelectedBlockClientId } = select( | ||
blockEditorStore | ||
); | ||
const selectedBlockClientId = getSelectedBlockClientId(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We are exploring ways to absorb parent controls as proposed in #26313. The fact that this panel depends on the currently selected block would prevent it from being usable in such context. Maybe it's fine because we might never get to the point where you could expose those controls from the parent to child blocks. It might be also not hard to refactor it when needed and pass the parent client id through the slot when necessary. I mostly wanted to raise awareness. I don't think we need to worry that much about it in the initial implementation, but we should keep that in mind that this panel doesn't necessarily need to be concerned only about the currently selected block. |
||
|
||
return { | ||
clientId: selectedBlockClientId, | ||
attributes: getBlockAttributes( selectedBlockClientId ), | ||
}; | ||
} ); | ||
const { updateBlockAttributes } = useDispatch( blockEditorStore ); | ||
|
||
const resetAll = ( resetFilters = [] ) => { | ||
const { style } = attributes; | ||
let newAttributes = { style }; | ||
|
||
resetFilters.forEach( ( resetFilter ) => { | ||
newAttributes = { | ||
...newAttributes, | ||
...resetFilter( newAttributes ), | ||
}; | ||
} ); | ||
|
||
// Enforce a cleaned style object. | ||
newAttributes = { | ||
...newAttributes, | ||
style: cleanEmptyObject( newAttributes.style ), | ||
}; | ||
|
||
updateBlockAttributes( clientId, newAttributes ); | ||
}; | ||
|
||
return ( | ||
<ToolsPanel | ||
label={ label } | ||
header={ header } | ||
resetAll={ resetAll } | ||
key={ clientId } | ||
panelId={ clientId } | ||
> | ||
{ children } | ||
</ToolsPanel> | ||
); | ||
} |
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.
Why do we use
bubblesVirtuall = { false }
. Ideally we always use true as we were thinking of deprecating the other approach ultimately.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 found when
bubblesVirtually
wastrue
it interfered with the registration of panel items via theToolsPanel
's context.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's a know limitation that you need to propagate the context when using slots that bubble virtually. An example when the context is passed down through
fillProps
is for the block controls:https://github.com/WordPress/gutenberg/blob/trunk/packages/block-editor/src/components/block-controls/slot.js
@diegohaz might remember better the implementation details. I'm not sure how much it differs from the use case you have, but we should try to generalize the solution if possible.
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.
Thanks @gziolo, the example you gave has helped get me most of the way to fixing this up.
The only real difference I see in this use case is that the
ToolsPanelContext
isn't being created until theToolsPanel
is when it wraps the Slot. I believe this means we need to wrap the Slot again so that component can grab the context and pass it through thefillProps
.I tried a HoC approach here but couldn't get that working. Instead, I have a regular component handling this. It almost "works" except that it introduces a rogue
<div>
wrapping all the fills. This breaks the layout of theToolsPanel
. I haven't been able to put my finger on where I've gone wrong here.Any help you can offer here would be greatly appreciated 🙏
Also, would this be something I could continue to iterate on in a separate 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.
If everything else is working then you can tackle it separately. It's Important to find a good solution here because the same approach is going to be replicated in other places 😄
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.
How is the component tree structured? Won't the Slot component be rendered inside ToolsPanel, in which case it would have access to the parent context?
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 PR's current state is working well according to the most recent reviews and testing. Myself, @glendaviesnz and @andrewserong have all put it through its paces.
If you are comfortable with this being merged, including the
bubblesVirtually={ false }
, I'll definitely continue iterating on this separately until that good solution is found.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 a bit out of my normal wheelhouse so please excuse my lack of understanding around this.
That was what I was originally expecting but it only worked when preventing virtual bubbling.
The ToolsPanel creates the context and wraps its children in the context provider. The slot is rendered within that. The panel items are added via that slot. Those panel items need to access the ToolsPanelContext to register themselves with the panel.
My current understanding is that the slot will have access to the context but the context has to be passed through
fillProps
so that we can re-create the context provider to wrap the fills thereby allowing the panel items access to the context again.I hope that makes sense!