Skip to content
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

Merged
merged 5 commits into from
Jan 26, 2018
Merged

Conversation

jorgefilipecosta
Copy link
Member

@jorgefilipecosta jorgefilipecosta commented Jan 24, 2018

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).

@jorgefilipecosta jorgefilipecosta self-assigned this Jan 24, 2018
@jorgefilipecosta jorgefilipecosta added [Type] Bug An existing feature does not function as intended [Type] Enhancement A suggestion for improvement. labels Jan 24, 2018
import { getCurrentPostType, getEditedPostAttribute } from '../../store/selectors';

export function PageAttributesOrder( { onUpdateOrder, instanceId, order, postType } ) {
if ( ! get( postType, 'data.supports.page-attributes', false ) ) {
Copy link
Contributor

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?

Copy link
Member Author

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">
Copy link
Contributor

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
Copy link
Member

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 */
Copy link
Member

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>
Copy link
Member

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>
Copy link
Member

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 || '' } );
Copy link
Member

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?

Copy link
Member Author

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 ) {
Copy link
Member

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?

@jorgefilipecosta jorgefilipecosta force-pushed the add/page-template branch 5 times, most recently from 43fbd09 to fd7c2ef Compare January 24, 2018 23:26
@jorgefilipecosta
Copy link
Member Author

jorgefilipecosta commented Jan 24, 2018

Hi @youknowriad, @aduth thank you for your reviews, most of the feedback was addressed.

'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'] ),
Copy link
Member

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.

Copy link
Member Author

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.

@pento
Copy link
Member

pento commented Jan 25, 2018

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.

@@ -28,24 +28,33 @@ export function PageAttributesOrder( { onUpdateOrder, instanceId, order } ) {
const inputId = `editor-page-attributes__order-${ instanceId }`;

return (
<PageAttributesCheck>
<label htmlFor={ inputId }>
<div>
Copy link
Member

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.

Copy link
Member Author

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 ) {
Copy link
Member

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 );

Copy link
Member Author

@jorgefilipecosta jorgefilipecosta Jan 25, 2018

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.

}

// TODO: Refactor to use a future ifPostTypeSupports HoC
function PageAttributesOrderWithChecks( props ) {
Copy link
Member

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.

'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'] ) ),
Copy link
Contributor

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?

Copy link
Member Author

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
Copy link
Contributor

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 ),
Copy link
Member

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={ {
Copy link
Member

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 );
Copy link
Member

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.

( state ) => {
return {
isOpened: isEditorSidebarPanelOpened( state, PANEL_NAME ),
postTypeSlug: getCurrentPostType( state ),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can be removed, too.

Copy link
Member

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( [
Copy link
Member

@gziolo gziolo Jan 25, 2018

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 :)

Copy link
Contributor

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 :)

Copy link
Member

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 ) => (
Copy link
Member

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 :.

Copy link
Member Author

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.

Copy link
Member

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 :)

Copy link
Member

@gziolo gziolo left a 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! 🚢

@jorgefilipecosta jorgefilipecosta force-pushed the add/page-template branch 2 times, most recently from c73cfa7 to 9205627 Compare January 25, 2018 17:21
…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.
This solves a bug, where Post Attributes core box was being show in "post" post type while it should be disabled.
@jorgefilipecosta
Copy link
Member Author

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.

@jorgefilipecosta jorgefilipecosta merged commit 772176b into master Jan 26, 2018
@jorgefilipecosta jorgefilipecosta deleted the add/page-template branch January 26, 2018 11:34
@mtias
Copy link
Member

mtias commented Jan 26, 2018

Nice :)

@mtias mtias mentioned this pull request Feb 7, 2018
9 tasks
@samikerb samikerb mentioned this pull request Jul 29, 2020
3 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
[Type] Bug An existing feature does not function as intended [Type] Enhancement A suggestion for improvement.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants