-
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
Add Custom Fields and Advanced Panels to Options modal #10676
Conversation
@youknowriad @aduth @gziolo: This is a first pass at completing #10210. Would greatly appreciate your thoughts on the overall direction1 before I work on adding tests and documentation. 1 Spoiler: it's hacky as hell 🙂 |
Allows the user to enable or disable 'Advanced Panels' (AKA Meta Boxes) via the Options modal. - The title and ID of each Meta Box is stored in the `core/edit-post` Redux store. - The `MetaBoxTitles` HOC provides an easy way to list all of the registered Meta Boxes within Gutenberg. - The `MetaBoxesVisibility` and `MetaBoxVisibility` components respond to the existing `isEditorPanelEnabled` selector and show/hide Meta Boxes using `el.style.display = 'none'`;
f864dc0
to
1779ddc
Compare
Re-enable the Custom Fields Meta Box which is included in Core, but have the panel disabled by default.
I've added the ability to enable and disable Custom Fields (disabled by default) and given the PR a more detailed description. Once I have a 👍 on the general direction here I'll start on the remaining tasks which are outlined above. |
I think the general direction is very good. It isn't an easy task to get more control over Meta Boxes :) @youknowriad spent weeks polishing the way it works and integrates with Gutenberg. I will leave some comments on the areas where I feel needs to be well documented to make it easier to maintain in the future. My first impression is that it should be emphasized that <MetaBoxTitles
renderItem={ ( title, id ) => (
<EnablePanelOption label={ title } panelName={ `meta-box-${ id }` } />
) }
/> We use this pattern in other places like |
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.
Left some comments, the general approach is good for me. My comments are mainly refactoring comments that can be done separately if needed.
lib/meta-box-partial-page.php
Outdated
@@ -328,6 +340,7 @@ function the_gutenberg_metaboxes() { | |||
*/ | |||
$script = 'window._wpLoadGutenbergEditor.then( function() { | |||
wp.data.dispatch( \'core/edit-post\' ).setActiveMetaBoxLocations( ' . wp_json_encode( $active_meta_box_locations ) . ' ); | |||
wp.data.dispatch( \'core/edit-post\' ).setMetaBoxTitles( ' . wp_json_encode( $meta_box_titles ) . ' ); |
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 name feels a bit weird to me. Is this really setMetaBoxTitles
or more something like setAvailableMetaBoxes
?
Also, maybe more logical to invert these two dispatches, as we should initialize the available metaboxes first and then tell it which one is active?
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.
We probably could also consolidate into one action since you proposed to have only one reducer.
@@ -91,6 +92,7 @@ function Layout( { | |||
<div className="edit-post-layout__metaboxes"> | |||
<MetaBoxes location="advanced" /> | |||
</div> | |||
<MetaBoxesVisibility /> |
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 really useful as its own isolated component? Should this be moved inside the MetaBoxes
component instead? Granted, it will force us to filter it per location.
} | ||
|
||
return state; | ||
} |
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.
Can we do a small refactoring to all these meta box states? I think we should just unify under a unique reducer (or combineReducers). Something like:
const metaboxes = {
isSaving: true,
locations: {
side: {
isActive: true,
metaboxes: [ // this could also be keyed by id
{ id: 'yoast', label: 'Yoast' }
]
}
}
}
It's not crucial though but I feel we'd gain in clarity by rethinking these states. Thoughts?
@@ -239,6 +239,10 @@ export function isMetaBoxLocationActive( state, location ) { | |||
return getActiveMetaBoxLocations( state ).includes( location ); | |||
} | |||
|
|||
export function getMetaBoxTitles( state ) { |
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.
Needs a JSDoc (and doc generation)
@@ -220,6 +220,13 @@ export function setActiveMetaBoxLocations( locations ) { | |||
}; | |||
} | |||
|
|||
export function setMetaBoxTitles( titles ) { |
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.
Need JSDoc and Doc generation
import { Fragment } from '@wordpress/element'; | ||
import { withSelect } from '@wordpress/data'; | ||
|
||
function MetaBoxTitles( { titles, titleWrapper } ) { |
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, I'm not certain what value this component brings, as it seems like we already have the selector as an abstraction to get the meta-boxes titles
I'm going to try to push this to the finish line according to my comments to land in 4.1. I hope you agree with those @noisysocks |
Actually, I think there's a conceptual issue in this PR because we don't update the "active metabox locations" when we show/hide meta boxes. I'm going to try to fix it though. |
I've updated the PR. We can't add the "custom fields" meta box yet because this would have the side effect of adding a new "save" request for all Gutenberg installs and we want to optimize for metabox-less experience. So we'd likely need to handle it as a special case in this modal (triggering this option or button would reload the page with the meta box enabled) I'd appreciate some manual testing with ACF for instance. I still need to add tests and polish deprecations. |
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.
We should audit all updated selectors and ensure all those which return arrays won't cause performance issues.
return get( | ||
state.metaBoxes.locations, | ||
[ location ], | ||
[] |
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 default value will have a different reference on every call.
We probably should have some a reusable const or helper which always returns the same instance of an empty array or object to avoid re-renders on every call.
* | ||
* @return {Array} List of meta boxes. | ||
*/ | ||
export function getAllMetaBoxes( state ) { |
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 we use memoization helper 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.
Memoization should be avoided if possible to avoid by shaping state such that we don't need to filter / map over it.
@@ -223,7 +223,25 @@ export function getMetaBoxes( state ) { | |||
* @return {string[]} Active meta box locations. | |||
*/ | |||
export function getActiveMetaBoxLocations( state ) { | |||
return state.activeMetaBoxLocations; | |||
return Object.keys( state.metaBoxes.locations ) |
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 we use memoization here?
return ( | ||
<Fragment> | ||
{ map( metaBoxes, ( { id } ) => ( | ||
<MetaBoxVisibility id={ id } /> |
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.
Does this not demand a key
?
Edit: Yes, there's a console 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.
It does, copy/paste :(
return get( | ||
state.metaBoxes.locations, | ||
[ location ], | ||
[] |
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 will return a new array each time (i.e. busting component purity). Previously I've used a top-of-scope constant variable.
allMetaBoxes = allMetaBoxes.concat( metaBoxes ); | ||
} ); | ||
|
||
return allMetaBoxes; |
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 new-array-each-time. Could state shape be optimized for direct return?
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.
No matter the shape of the reducer, it will impact one selector or the other.
Seems to be an issue on master as well, but ACF meta values aren't saving and a dirty prompt is shown when trying to reload. |
@aduth Funny because I was testing with an old version of ACF and it was working fine :) |
I'm going to consider it as an ACF bug that's out of scope of the current PR. It was also mentioned here #10660. |
@@ -236,9 +260,43 @@ export function getActiveMetaBoxLocations( state ) { | |||
* @return {boolean} Whether the meta box location is active. | |||
*/ | |||
export function isMetaBoxLocationActive( state, location ) { | |||
return getActiveMetaBoxLocations( state ).includes( location ); | |||
return getMetaBoxesPerLocation( state, location ).length !== 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.
getMetaBoxesPerLocation
is not guaranteed to return an array, at which point this throws an error.
{ map( metaBoxes, ( { id } ) => ( | ||
<MetaBoxVisibility key={ id } id={ id } /> | ||
) ) } | ||
{ isVisible && <MetaBoxesArea location={ location } /> } |
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 it necessary to render this component at all if ! isVisible
? i.e. an ifCondition
.
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 necessary to handle the visibility.
*/ | ||
export const getAllMetaBoxes = createSelector( | ||
( state ) => { | ||
let allMetaBoxes = []; |
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.
Possible simplification?
return flatten( values( state.metaBoxes.locations ) );
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.
Definitely
7aba1e3
to
673750e
Compare
Thanks for your help on this. @noisysocks Let's try to tackle #3228 as a follow-up using this approach or similar #10676 (comment) |
Thanks for taking over @youknowriad! I love the approach of handling everything in
👍 Will look into this. |
* First pass at adding 'Advanced Panels' to Options modal Allows the user to enable or disable 'Advanced Panels' (AKA Meta Boxes) via the Options modal.
What this does
Closes #10210 and closes #3228.
Firstly, this PR allows the user to enable or disable Advanced Panels (AKA Meta Boxes) via the Screen Options modal that was added in #10215.
Secondly, it adds Custom Fields to Gutenberg in the form of an Advanced Panel that is disabled by default.
How it works
core/edit-post
Redux store.MetaBoxTitles
HOC provides an easy way to list all of the registered Meta Boxes within Gutenberg.MetaBoxesVisibility
andMetaBoxVisibility
components respond to the existingisEditorPanelEnabled
selector and show/hide Meta Boxes usingel.style.display = 'none';
postcustom
) is no longer filtered out bygutenberg_filter_meta_boxes
How to test
Remaining tasks
.edit-post-layout__metaboxes
container from appearing when all Advanced Panels are disabled