Skip to content

Commit

Permalink
[Full Site Editing]: Expand the templates that can be added - custom …
Browse files Browse the repository at this point in the history
…taxonomies, specific term, specific category and tag
  • Loading branch information
ntsekouras committed Jul 1, 2022
1 parent 24acf99 commit b315644
Show file tree
Hide file tree
Showing 3 changed files with 386 additions and 136 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ import { mapToIHasNameAndId } from './utils';
const EMPTY_ARRAY = [];
const BASE_QUERY = {
order: 'asc',
_fields: 'id,title,slug,link',
_fields: 'id,name,title,slug,link',
context: 'view',
};

Expand All @@ -47,20 +47,33 @@ function SuggestionListItem( {
{ ...composite }
className={ baseCssClass }
onClick={ () => {
const {
labels,
slug,
config: { aliasTemplateSlug, templatePrefix },
} = entityForSuggestions;
// TODO: check if we can reuse the message and the translators message...(?)
// This refers to `where %1$s is the singular name of a post type and %2$s...`part.
const title = sprintf(
// translators: Represents the title of a user's custom template in the Site Editor, where %1$s is the singular name of a post type and %2$s is the name of the post, e.g. "Post: Hello, WordPress"
__( '%1$s: %2$s' ),
entityForSuggestions.labels.singular_name,
labels.singular_name,
suggestion.name
);
let _slug = `${ aliasTemplateSlug || slug }-${
suggestion.slug
}`;
if ( templatePrefix ) {
_slug = templatePrefix + _slug;
}
onSelect( {
title,
description: sprintf(
// translators: Represents the description of a user's custom template in the Site Editor, e.g. "Template for Post: Hello, WordPress"
__( 'Template for %1$s' ),
title
),
slug: `single-${ entityForSuggestions.slug }-${ suggestion.slug }`,
slug: _slug,
} );
} }
>
Expand All @@ -84,11 +97,12 @@ function SuggestionList( { entityForSuggestions, onSelect } ) {
const [ searchInputValue, setSearchInputValue ] = useState( '' );
const [ search, setSearch ] = useState( '' );
const debouncedSearch = useDebounce( setSearch, 250 );
const { config, postsToExclude, labels } = entityForSuggestions;
const query = {
...BASE_QUERY,
search,
orderby: search ? 'relevance' : 'modified',
exclude: entityForSuggestions.postsToExclude,
orderby: search ? config.orderBySearch : config.orderBy,
exclude: postsToExclude,
per_page: search ? 20 : 10,
};
const { records: searchResults, hasResolved: searchHasResolved } =
Expand All @@ -104,7 +118,10 @@ function SuggestionList( { entityForSuggestions, onSelect } ) {
}, [ search, searchInputValue ] );
const entitiesInfo = useMemo( () => {
if ( ! searchResults?.length ) return EMPTY_ARRAY;
return mapToIHasNameAndId( searchResults, 'title.rendered' );
if ( config.recordNamePath ) {
return mapToIHasNameAndId( searchResults, config.recordNamePath );
}
return searchResults;
}, [ searchResults ] );
// Update suggestions only when the query has resolved.
useEffect( () => {
Expand All @@ -116,8 +133,8 @@ function SuggestionList( { entityForSuggestions, onSelect } ) {
<SearchControl
onChange={ setSearchInputValue }
value={ searchInputValue }
label={ entityForSuggestions.labels.search_items }
placeholder={ entityForSuggestions.labels.search_items }
label={ labels.search_items }
placeholder={ labels.search_items }
/>
{ !! suggestions?.length && (
<Composite
Expand All @@ -139,7 +156,7 @@ function SuggestionList( { entityForSuggestions, onSelect } ) {
) }
{ search && ! suggestions?.length && (
<p className="edit-site-custom-template-modal__no-results">
{ entityForSuggestions.labels.not_found }
{ labels.not_found }
</p>
) }
</>
Expand Down
166 changes: 83 additions & 83 deletions packages/edit-site/src/components/add-new-template/new-template.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,3 @@
/**
* External dependencies
*/
import { filter, includes } from 'lodash';

/**
* WordPress dependencies
*/
Expand All @@ -13,9 +8,8 @@ import {
NavigableMenu,
} from '@wordpress/components';
import { useState } from '@wordpress/element';
import { useSelect, useDispatch } from '@wordpress/data';
import { useDispatch } from '@wordpress/data';
import { store as coreStore } from '@wordpress/core-data';
import { store as editorStore } from '@wordpress/editor';
import {
archive,
blockMeta,
Expand All @@ -31,14 +25,23 @@ import {
search,
tag,
} from '@wordpress/icons';
import { __, sprintf } from '@wordpress/i18n';
import { __ } from '@wordpress/i18n';
import { store as noticesStore } from '@wordpress/notices';

/**
* Internal dependencies
*/
import AddCustomTemplateModal from './add-custom-template-modal';
import { usePostTypes, usePostTypesEntitiesInfo } from './utils';
import {
useExistingTemplates,
useDefaultTemplateTypes,
entitiesConfig,
usePostTypes,
useTaxonomies,
useTaxonomyCategory,
useTaxonomyTag,
useExtraTemplates,
} from './utils';
import { useHistory } from '../routes';
import { store as editSiteStore } from '../../store';

Expand Down Expand Up @@ -75,27 +78,19 @@ const TEMPLATE_ICONS = {

export default function NewTemplate( { postType } ) {
const history = useHistory();
const postTypes = usePostTypes();
const [ showCustomTemplateModal, setShowCustomTemplateModal ] =
useState( false );
const [ entityForSuggestions, setEntityForSuggestions ] = useState( {} );
const { existingTemplates, defaultTemplateTypes } = useSelect(
( select ) => ( {
existingTemplates: select( coreStore ).getEntityRecords(
'postType',
'wp_template',
{ per_page: -1 }
),
defaultTemplateTypes:
select( editorStore ).__experimentalGetDefaultTemplateTypes(),
} ),
[]
);
const postTypesEntitiesInfo = usePostTypesEntitiesInfo( existingTemplates );
const { saveEntityRecord } = useDispatch( coreStore );
const { createErrorNotice } = useDispatch( noticesStore );
const { setTemplate } = useDispatch( editSiteStore );
const postTypes = usePostTypes();
const taxonomies = useTaxonomies();
const categoryTaxonomy = useTaxonomyCategory();
const tagTaxonomy = useTaxonomyTag();

const existingTemplates = useExistingTemplates();
const defaultTemplateTypes = useDefaultTemplateTypes();
async function createTemplate( template ) {
try {
const { title, description, slug } = template;
Expand Down Expand Up @@ -135,78 +130,83 @@ export default function NewTemplate( { postType } ) {
} );
}
}

const existingTemplateSlugs = ( existingTemplates || [] ).map(
( { slug } ) => slug
);
const missingTemplates = filter(
defaultTemplateTypes,

const missingDefaultTemplates = ( defaultTemplateTypes || [] ).filter(
( template ) =>
includes( DEFAULT_TEMPLATE_SLUGS, template.slug ) &&
! includes( existingTemplateSlugs, template.slug )
DEFAULT_TEMPLATE_SLUGS.includes( template.slug ) &&
! existingTemplateSlugs.includes( template.slug )
);

const extraTemplates = ( postTypes || [] ).reduce(
( accumulator, _postType ) => {
const { slug, labels, icon } = _postType;
const hasGeneralTemplate = existingTemplateSlugs?.includes(
`single-${ slug }`
);
const hasEntities = postTypesEntitiesInfo?.[ slug ]?.hasEntities;
const menuItem = {
slug: `single-${ slug }`,
title: sprintf(
// translators: %s: Name of the post type e.g: "Post".
__( 'Single item: %s' ),
labels.singular_name
),
description: sprintf(
// translators: %s: Name of the post type e.g: "Post".
__( 'Displays a single item: %s.' ),
labels.singular_name
),
// `icon` is the `menu_icon` property of a post type. We
// only handle `dashicons` for now, even if the `menu_icon`
// also supports urls and svg as values.
icon: icon?.startsWith( 'dashicons-' )
? icon.slice( 10 )
: null,
};
// We have a different template creation flow only if they have entities.
if ( hasEntities ) {
menuItem.onClick = ( template ) => {
setShowCustomTemplateModal( true );
setEntityForSuggestions( {
type: 'postType',
slug,
labels,
hasGeneralTemplate,
template,
postsToExclude:
postTypesEntitiesInfo[ slug ].existingPosts,
} );
};
}
// We don't need to add the menu item if there are no
// entities and the general template exists.
if ( ! hasGeneralTemplate || hasEntities ) {
accumulator.push( menuItem );
}
return accumulator;
},
[]
const onClickMenuItem = ( _entityForSuggestions ) => {
setShowCustomTemplateModal( true );
setEntityForSuggestions( _entityForSuggestions );
};
// TODO: find better names for many new vars :)
const categoryMenuItem = useExtraTemplates(
categoryTaxonomy,
entitiesConfig.category,
onClickMenuItem
);
if ( ! missingTemplates.length && ! extraTemplates.length ) {
return null;
}
const tagMenuItem = useExtraTemplates(
tagTaxonomy,
entitiesConfig.tag,
onClickMenuItem
);
// We need to replace existing default template types with
// the create specific template functionality. The original
// info(`title, description, etc..) are preserved in
// `useExtraTemplates` hook.
const enhancedMissingDefaultTemplateTypes = [ ...missingDefaultTemplates ];
[ categoryMenuItem, tagMenuItem ].forEach( ( menuItem ) => {
if ( ! menuItem?.length ) {
return;
}
const matchIndex = enhancedMissingDefaultTemplateTypes.findIndex(
( template ) => template.slug === menuItem[ 0 ].slug
);
// Some default template types might have been filtered above from
// `missingDefaultTemplates` because they only check for the general
// template. So here we either replace or append the item, augmented
// with the check if it has available specific item to create a
// template for.
if ( matchIndex > -1 ) {
enhancedMissingDefaultTemplateTypes.splice(
matchIndex,
1,
menuItem[ 0 ]
);
} else {
enhancedMissingDefaultTemplateTypes.push( menuItem[ 0 ] );
}
} );
// Update the sort order to match the DEFAULT_TEMPLATE_SLUGS order.
missingTemplates?.sort( ( template1, template2 ) => {
enhancedMissingDefaultTemplateTypes?.sort( ( template1, template2 ) => {
return (
DEFAULT_TEMPLATE_SLUGS.indexOf( template1.slug ) -
DEFAULT_TEMPLATE_SLUGS.indexOf( template2.slug )
);
} );
// Append all extra templates at the end of the list for now.
missingTemplates.push( ...extraTemplates );
const extraPostTypeTemplates = useExtraTemplates(
postTypes,
entitiesConfig.postType,
onClickMenuItem
);
const extraTaxonomyTemplates = useExtraTemplates(
taxonomies,
entitiesConfig.taxonomy,
onClickMenuItem
);
const missingTemplates = [
...enhancedMissingDefaultTemplateTypes,
...extraPostTypeTemplates,
...extraTaxonomyTemplates,
];
if ( ! missingTemplates.length ) {
return null;
}
return (
<>
<DropdownMenu
Expand Down
Loading

0 comments on commit b315644

Please sign in to comment.