-
Notifications
You must be signed in to change notification settings - Fork 4.3k
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 ability to set page/post template #4662
Conversation
import { getCurrentPostType, getEditedPostAttribute } from '../../store/selectors'; | ||
|
||
export function PageAttributesOrder( { onUpdateOrder, instanceId, order, postType } ) { | ||
if ( ! get( postType, 'data.supports.page-attributes', false ) ) { |
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 have a component for these checks PostTypeSupportCheck
, can we reuse it 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.
Hi @youknowriad, PostTypeSupportCheck was updated in this PR to use the same condition used in core to decide when to show Post Attributes metabox: ( ! supportsPageAttributes && isEmpty( availableTemplates ).
In this case, we really want check if post type supports page attributes because it is the condition also used in the core to check when to show Order input field.
|
||
/* eslint-disable jsx-a11y/no-onchange */ | ||
return ( | ||
<div className="editor-page-template"> |
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 this be editor-page-attributes__template
to match our CSS guidelines?
} | ||
const selectId = `template-selector-${ instanceId }`; | ||
|
||
// Disable reason: A select with an onchange throws a warning |
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 statement of fact, not a reason for disabling.
|
||
// Disable reason: A select with an onchange throws a warning | ||
|
||
/* eslint-disable jsx-a11y/no-onchange */ |
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 are never re-enabling the ESLint rule.
onChange={ ( event ) => onUpdateTemplate( event.target.value ) } | ||
> | ||
{ map( { '': __( 'Default template' ), ...availableTemplates }, ( templateName, templateSlug ) => ( | ||
<option key={ templateSlug || 'default' } value={ templateSlug }>{ templateName }</option> |
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 an empty string key
invalid? Wonder whether we need the || 'default'
fallback.
onChange={ ( event ) => onUpdateTemplate( event.target.value ) } | ||
> | ||
{ map( { '': __( 'Default template' ), ...availableTemplates }, ( templateName, templateSlug ) => ( | ||
<option key={ templateSlug || 'default' } value={ templateSlug }>{ templateName }</option> |
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 an empty string key
invalid? Wonder whether we need the || 'default'
fallback.
}, | ||
{ | ||
onUpdateTemplate( templateSlug ) { | ||
return editPost( { template: templateSlug || '' } ); |
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 explicitly set the template to the empty string, or is there a way we can "unset" its value to not send anything at all in the save request?
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 problem with not sending anything at all in the save request is that if we had selected a template before and now we want to use the default one, if we don't send an empty string in the request e.g: don't send anything at all for the template, we will not be updating it and the template used before will continue to be used.
}; | ||
}, | ||
{ | ||
onUpdateTemplate( templateSlug ) { |
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.
Could the Template
suffix be considered redundant if it's clear by the component name itself that given, <PageTemplate onChange={} />
, onChange
would be called when the template is updated?
43fbd09
to
fd7c2ef
Compare
Hi @youknowriad, @aduth thank you for your reviews, most of the feedback was addressed. |
lib/client-assets.php
Outdated
'blockTypes' => $allowed_block_types, | ||
'titlePlaceholder' => apply_filters( 'enter_title_here', __( 'Add title', 'gutenberg' ), $post ), | ||
'alignWide' => $align_wide || ! empty( $gutenberg_theme_support[0]['wide-images'] ), // Backcompat. Use `align-wide` outside of `gutenberg` array. | ||
'availableTemplates' => wp_get_theme()->get_page_templates( $post_to_edit['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.
get_page_templates()
requires a WP_Post
, sending it an ID does work, but it doesn't convert the ID to a WP_Post
internally, for sending to the theme_{$post_type}_templates
filter.
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.
Hi @pento, thank you for the feedback, I added a call to WP_Post::get_instance so we can correctly pass a WP_Post instance to the filters.
The one comment aside, I really like this approach. It gets things working now, it isn't necessary to implement the harder solutions (eg, #2086) until well after WordPress 5.0, when we might need to be able to dynamically switch which post is being edited. |
fd7c2ef
to
4aa2b75
Compare
@@ -28,24 +28,33 @@ export function PageAttributesOrder( { onUpdateOrder, instanceId, order } ) { | |||
const inputId = `editor-page-attributes__order-${ instanceId }`; | |||
|
|||
return ( | |||
<PageAttributesCheck> | |||
<label htmlFor={ inputId }> | |||
<div> |
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 would expect one or the other of a div
wrapper, or array returns with key on each children, but not both. Alternatively, replacing the div
with wp.element.Fragment
and dropping the key
. My preference would be toward one of the latter two options.
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.
Sorry, I was switching to the array approach, I got problems in the tests and then I used another approach but I missed the revert of key changes :( I used the Fragment approach thank you for the suggestion 👍
); | ||
} | ||
|
||
function PageAttributesOrderWithChecks( props ) { |
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.
Aside: Not usually a fan of multiple components defined in a single file, and this particular use makes me curious if an HoC pattern for checks could be simpler to integrate into our compose
convention:
export default compose( [
ifPostTypeSupports( [ 'page-attributes' ] ),
] )( PageAttributesOrder );
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 with the suggestion, as future work an HoC will be implemented and used in this and other cases.
4aa2b75
to
0438b47
Compare
} | ||
|
||
// TODO: Refactor to use a future ifPostTypeSupports HoC | ||
function PageAttributesOrderWithChecks( props ) { |
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 probably should go to its own file and be converted to HOC when refactoring.
lib/client-assets.php
Outdated
'blockTypes' => $allowed_block_types, | ||
'titlePlaceholder' => apply_filters( 'enter_title_here', __( 'Add title', 'gutenberg' ), $post ), | ||
'alignWide' => $align_wide || ! empty( $gutenberg_theme_support[0]['wide-images'] ), // Backcompat. Use `align-wide` outside of `gutenberg` array. | ||
'availableTemplates' => wp_get_theme()->get_page_templates( WP_Post::get_instance( $post_to_edit['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.
Why WP_Post::get_instance
? Why not get_post
?
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.
Updated :)
); | ||
} | ||
|
||
// TODO: Refactor to use a future ifPostTypeSupports HoC |
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.
Trying out a HoC (per #4662) sounds fine, but until then I don't think there's a point to keeping this comment.
); | ||
} | ||
|
||
const applyConnect = connect( | ||
( state ) => { | ||
return { | ||
postTypeSlug: getCurrentPostType( 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.
It's not used in this file. Should we remove it or maybe move to the component that uses it?
const wrapper = shallow( | ||
<PageAttributesCheck postType={ { | ||
<PageAttributesCheck availableTemplates={ {} } postType={ { |
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.
Would it make sense to provide a default value for availableTemplates
?
it( 'should render if page attributes support is true and no available templates exist', () => { | ||
const wrapper = shallow( <PageAttributesCheck availableTemplates={ {} } postType={ postType }>content</PageAttributesCheck> ); | ||
|
||
expect( wrapper.type() ).not.toBe( null ); |
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'm wondering if having test like:
expect( wrapper.find( 'PageAttributesCheck' ) ).toHaveText( 'content' );
// not sure it works though :)
would better document the behavior of the component.
0438b47
to
8488af1
Compare
( state ) => { | ||
return { | ||
isOpened: isEditorSidebarPanelOpened( state, PANEL_NAME ), | ||
postTypeSlug: getCurrentPostType( 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 be removed, too.
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.
OOps, it is referenced in withAPIData
:)
} ) | ||
); | ||
|
||
export default compose( [ |
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.
You don't need to use an array with compose
:
export default compose(
applyConnect,
applyWithContext,
withInstanceId
)( PageTemplate );
Is fine, too :)
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 always go back and forth on this one. I was going to say that one upside of passing an array is that all functions in it can be formatted alike, notably with a trailing comma, but it seems that with our current linting + build rules the following is accepted:
export default compose(
applyFoo,
applyBar, // notice trailing comma
)( FooBar );
TIL :)
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.
Right, you can now add trailing comma when using ES.next 👍
onBlur={ onEventUpdate } | ||
onChange={ onEventUpdate } | ||
> | ||
{ map( { '': __( 'Default template' ), ...availableTemplates }, ( templateName, templateSlug ) => ( |
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.
Nit: space missing after ''
, before :
.
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 we add the space e.g make things like ''' : __( 'Default template' )' we get a linting error because of the extra space.
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.
Skip that, I thought it is if
statement :)
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 works as expected, it looks like all the major issues raised comments were addressed. In my opinion, it is ready to go. Good job! 🚢
c73cfa7
to
9205627
Compare
9205627
to
b66abdd
Compare
…or if post type has available templates. This condition is the same as the one used in core. This solves the bug of not being able to change the post template even if there are post templates available.
…me condition used in core.
This solves a bug, where Post Attributes core box was being show in "post" post type while it should be disabled.
b66abdd
to
9af433d
Compare
Hi team, I rebased this PR to take into account the changes in https://github.com/WordPress/gutenberg/pull/4661/files. The changes are minor since the review, the most relevant changes are the addition of getCurrentPostType to the list of exposed selectors and the usage of our query HoC to retrieve the postType. |
Nice :) |
Description
The main objective of this PR is to add the ability to change page/post template.
Fixes: #991
How Has This Been Tested?
Verify your theme has custom page templates. If not create a new file in the theme folder with a simple template:
<?php /* Template Name: Test templage Template Post Type: post, page, event */ ?> test
. (Change the theme and recharge to it if the new template is not available.Set a page template. Save the post verify the page template gets used.
Do the same process for a "post" post type, verify the panel is name "Post Attributes" instead of "Page Attributes".
Verify the Post Attributes meta box is not appearing in advanced post settings.
Types of changes
Used correct label, for Post, attributes from the post types label.
Removed Post Attributes default core meta box.
Corrected "Page Attributes" panel to show if post type supports page attributes or if the there are custom templates available (same logic as in core).