Skip to content

Commit

Permalink
Implement "Add New" for templates list in Site Editor (#36592)
Browse files Browse the repository at this point in the history
* Add "Add New" feature for templates list in Site Editor

* Fix e2e test

* Fix modal not opened when making template part

* Code review

* Fix unit test
  • Loading branch information
kevin940726 authored and noisysocks committed Nov 22, 2021
1 parent 9740993 commit e53ef2b
Show file tree
Hide file tree
Showing 16 changed files with 436 additions and 174 deletions.
30 changes: 17 additions & 13 deletions lib/full-site-editing/edit-site-page.php
Expand Up @@ -78,8 +78,10 @@ function gutenberg_get_editor_styles() {

/**
* Initialize the Gutenberg Templates List Page.
*
* @param array $settings The editor settings.
*/
function gutenberg_edit_site_list_init() {
function gutenberg_edit_site_list_init( $settings ) {
wp_enqueue_script( 'wp-edit-site' );
wp_enqueue_style( 'wp-edit-site' );
wp_enqueue_media();
Expand Down Expand Up @@ -111,10 +113,11 @@ function gutenberg_edit_site_list_init() {
'wp-edit-site',
sprintf(
'wp.domReady( function() {
wp.editSite.initializeList( "%s", "%s" );
wp.editSite.initializeList( "%s", "%s", %s );
} );',
'edit-site-editor',
$template_type
$template_type,
wp_json_encode( $settings )
)
);
}
Expand All @@ -133,8 +136,18 @@ function gutenberg_edit_site_init( $hook ) {
return;
}

$custom_settings = array(
'siteUrl' => site_url(),
'postsPerPage' => get_option( 'posts_per_page' ),
'styles' => gutenberg_get_editor_styles(),
'defaultTemplateTypes' => gutenberg_get_indexed_default_template_types(),
'defaultTemplatePartAreas' => get_allowed_block_template_part_areas(),
'__experimentalBlockPatterns' => WP_Block_Patterns_Registry::get_instance()->get_all_registered(),
'__experimentalBlockPatternCategories' => WP_Block_Pattern_Categories_Registry::get_instance()->get_all_registered(),
);

if ( gutenberg_is_edit_site_list_page() ) {
return gutenberg_edit_site_list_init();
return gutenberg_edit_site_list_init( $custom_settings );
}

/**
Expand All @@ -145,15 +158,6 @@ function gutenberg_edit_site_init( $hook ) {
*/
$current_screen->is_block_editor( true );

$custom_settings = array(
'siteUrl' => site_url(),
'postsPerPage' => get_option( 'posts_per_page' ),
'styles' => gutenberg_get_editor_styles(),
'defaultTemplateTypes' => gutenberg_get_indexed_default_template_types(),
'defaultTemplatePartAreas' => get_allowed_block_template_part_areas(),
'__experimentalBlockPatterns' => WP_Block_Patterns_Registry::get_instance()->get_all_registered(),
'__experimentalBlockPatternCategories' => WP_Block_Pattern_Categories_Registry::get_instance()->get_all_registered(),
);
$site_editor_context = new WP_Block_Editor_Context();
$settings = gutenberg_get_block_editor_settings( $custom_settings, $site_editor_context );
$active_global_styles_id = WP_Theme_JSON_Resolver_Gutenberg::get_user_custom_post_type_id();
Expand Down
2 changes: 1 addition & 1 deletion packages/base-styles/_z-index.scss
Expand Up @@ -139,7 +139,7 @@ $z-layers: (

// Should be above the popover (dropdown)
".reusable-blocks-menu-items__convert-modal": 1000001,
".edit-site-template-part-converter__modal": 1000001,
".edit-site-create-template-part-modal": 1000001,

// ...Except for popovers immediately beneath wp-admin menu on large breakpoints
".components-popover.block-editor-inserter__popover": 99999,
Expand Down
2 changes: 1 addition & 1 deletion packages/e2e-tests/specs/experiments/template-part.test.js
Expand Up @@ -19,7 +19,7 @@ import {
import { siteEditor } from '../../experimental-features';

const templatePartNameInput =
'.edit-site-template-part-converter__modal .components-text-control__input';
'.edit-site-create-template-part-modal .components-text-control__input';

describe( 'Template Part', () => {
beforeAll( async () => {
Expand Down
30 changes: 30 additions & 0 deletions packages/edit-site/src/components/add-new-template/index.js
@@ -0,0 +1,30 @@
/**
* WordPress dependencies
*/
import { useSelect } from '@wordpress/data';
import { store as coreStore } from '@wordpress/core-data';

/**
* Internal dependencies
*/
import NewTemplate from './new-template';
import NewTemplatePart from './new-template-part';

export default function AddNewTemplate( { templateType = 'wp_template' } ) {
const postType = useSelect(
( select ) => select( coreStore ).getPostType( templateType ),
[ templateType ]
);

if ( ! postType ) {
return null;
}

if ( templateType === 'wp_template' ) {
return <NewTemplate postType={ postType } />;
} else if ( templateType === 'wp_template_part' ) {
return <NewTemplatePart postType={ postType } />;
}

return null;
}
@@ -0,0 +1,67 @@
/**
* External dependencies
*/
import { kebabCase } from 'lodash';

/**
* WordPress dependencies
*/
import { useState } from '@wordpress/element';
import { Button } from '@wordpress/components';
import { addQueryArgs } from '@wordpress/url';
import apiFetch from '@wordpress/api-fetch';

/**
* Internal dependencies
*/
import CreateTemplatePartModal from '../create-template-part-modal';

export default function NewTemplatePart( { postType } ) {
const [ isModalOpen, setIsModalOpen ] = useState( false );

async function createTemplatePart( { title, area } ) {
if ( ! title ) {
return;
}

const templatePart = await apiFetch( {
path: '/wp/v2/template-parts',
method: 'POST',
data: {
slug: kebabCase( title ),
title,
content: '',
area,
},
} );

// Navigate to the created template part editor.
window.location.search = addQueryArgs( '', {
page: 'gutenberg-edit-site',
postId: templatePart.id,
postType: 'wp_template_part',
} );

// Wait for async navigation to happen before closing the modal.
await new Promise( () => {} );
}

return (
<>
<Button
variant="primary"
onClick={ () => {
setIsModalOpen( true );
} }
>
{ postType.labels.add_new }
</Button>
{ isModalOpen && (
<CreateTemplatePartModal
closeModal={ () => setIsModalOpen( false ) }
onCreate={ createTemplatePart }
/>
) }
</>
);
}
117 changes: 117 additions & 0 deletions packages/edit-site/src/components/add-new-template/new-template.js
@@ -0,0 +1,117 @@
/**
* External dependencies
*/
import { filter, find, includes, map } from 'lodash';

/**
* WordPress dependencies
*/
import {
DropdownMenu,
MenuGroup,
MenuItem,
NavigableMenu,
} from '@wordpress/components';
import { useSelect } from '@wordpress/data';
import { store as coreStore } from '@wordpress/core-data';
import { store as editorStore } from '@wordpress/editor';
import { addQueryArgs } from '@wordpress/url';
import apiFetch from '@wordpress/api-fetch';

const DEFAULT_TEMPLATE_SLUGS = [
'front-page',
'single-post',
'page',
'archive',
'search',
'404',
'index',
];

export default function NewTemplate( { postType } ) {
const { templates, defaultTemplateTypes } = useSelect(
( select ) => ( {
templates: select( coreStore ).getEntityRecords(
'postType',
'wp_template'
),
defaultTemplateTypes: select(
editorStore
).__experimentalGetDefaultTemplateTypes(),
} ),
[]
);

async function createTemplate( { slug } ) {
const { title, description } = find( defaultTemplateTypes, { slug } );

const template = await apiFetch( {
path: '/wp/v2/templates',
method: 'POST',
data: {
excerpt: description,
// Slugs need to be strings, so this is for template `404`
slug: slug.toString(),
status: 'publish',
title,
},
} );

// Navigate to the created template editor.
window.location.search = addQueryArgs( '', {
page: 'gutenberg-edit-site',
postId: template.id,
postType: 'wp_template',
} );
}

const existingTemplateSlugs = map( templates, 'slug' );

const missingTemplates = filter(
defaultTemplateTypes,
( template ) =>
includes( DEFAULT_TEMPLATE_SLUGS, template.slug ) &&
! includes( existingTemplateSlugs, template.slug )
);

if ( ! missingTemplates.length ) {
return null;
}

return (
<DropdownMenu
className="edit-site-new-template-dropdown"
icon={ null }
text={ postType.labels.add_new }
label={ postType.labels.add_new_item }
popoverProps={ {
noArrow: false,
} }
toggleProps={ {
variant: 'primary',
} }
>
{ () => (
<NavigableMenu className="edit-site-new-template-dropdown__popover">
<MenuGroup label={ postType.labels.add_new_item }>
{ map(
missingTemplates,
( { title, description, slug } ) => (
<MenuItem
info={ description }
key={ slug }
onClick={ () => {
createTemplate( { slug } );
// We will be navigated way so no need to close the dropdown.
} }
>
{ title }
</MenuItem>
)
) }
</MenuGroup>
</NavigableMenu>
) }
</DropdownMenu>
);
}
11 changes: 11 additions & 0 deletions packages/edit-site/src/components/add-new-template/style.scss
@@ -0,0 +1,11 @@
.edit-site-new-template-dropdown {
.components-dropdown-menu__toggle {
padding: 6px 12px;
}

.edit-site-new-template-dropdown__popover {
@include break-small() {
min-width: 300px;
}
}
}

0 comments on commit e53ef2b

Please sign in to comment.