diff --git a/packages/e2e-tests/specs/experiments/multi-entity-editing.test.js b/packages/e2e-tests/specs/experiments/multi-entity-editing.test.js index 2d61fa19729e1..7e1b127b0be67 100644 --- a/packages/e2e-tests/specs/experiments/multi-entity-editing.test.js +++ b/packages/e2e-tests/specs/experiments/multi-entity-editing.test.js @@ -204,8 +204,11 @@ describe( 'Multi-entity editor states', () => { '.wp-block-template-part .block-editor-block-list__layout' ); - // Our custom template shows up in the " templates > all" menu; let's use it. - await clickTemplateItem( [ 'Templates', 'All' ], templateName ); + // Our custom template shows up in the "Templates > General" menu; let's use it. + await clickTemplateItem( + [ 'Templates', 'General templates' ], + templateName + ); await page.waitForXPath( `//h1[contains(@class, "edit-site-document-actions__title") and contains(text(), '${ templateName }')]` ); diff --git a/packages/edit-site/src/components/navigation-sidebar/navigation-panel/constants.js b/packages/edit-site/src/components/navigation-sidebar/navigation-panel/constants.js index bf88a77aa4a33..ea6c799bed779 100644 --- a/packages/edit-site/src/components/navigation-sidebar/navigation-panel/constants.js +++ b/packages/edit-site/src/components/navigation-sidebar/navigation-panel/constants.js @@ -3,16 +3,43 @@ */ import { __ } from '@wordpress/i18n'; -export const TEMPLATES_GENERAL = [ - 'front-page', - 'archive', - 'singular', +export const TEMPLATES_PRIMARY = [ 'index', - 'search', + 'singular', + 'archive', + 'single', + 'page', + 'home', '404', + 'search', +]; + +export const TEMPLATES_SECONDARY = [ + 'author', + 'category', + 'taxonomy', + 'date', + 'tag', + 'attachment', + 'single-post', + 'front-page', +]; + +export const TEMPLATES_TOP_LEVEL = [ + ...TEMPLATES_PRIMARY, + ...TEMPLATES_SECONDARY, +]; + +export const TEMPLATES_GENERAL = [ 'page-home' ]; + +export const TEMPLATES_POSTS_PREFIXES = [ + 'post-', + 'author-', + 'single-post-', + 'tag-', ]; -export const TEMPLATES_POSTS = [ 'home', 'single', 'single-post' ]; +export const TEMPLATES_PAGES_PREFIXES = [ 'page-' ]; export const TEMPLATES_NEW_OPTIONS = [ 'front-page', @@ -30,9 +57,10 @@ export const MENU_CONTENT_PAGES = 'content-pages'; export const MENU_CONTENT_POSTS = 'content-posts'; export const MENU_TEMPLATE_PARTS = 'template-parts'; export const MENU_TEMPLATES = 'templates'; -export const MENU_TEMPLATES_ALL = 'templates-all'; +export const MENU_TEMPLATES_GENERAL = 'templates-general'; export const MENU_TEMPLATES_PAGES = 'templates-pages'; export const MENU_TEMPLATES_POSTS = 'templates-posts'; +export const MENU_TEMPLATES_UNUSED = 'templates-unused'; export const SEARCH_DEBOUNCE_IN_MS = 75; diff --git a/packages/edit-site/src/components/navigation-sidebar/navigation-panel/menus/templates-pages.js b/packages/edit-site/src/components/navigation-sidebar/navigation-panel/menus/templates-pages.js deleted file mode 100644 index d4816219a0239..0000000000000 --- a/packages/edit-site/src/components/navigation-sidebar/navigation-panel/menus/templates-pages.js +++ /dev/null @@ -1,49 +0,0 @@ -/** - * External dependencies - */ -import { map } from 'lodash'; - -/** - * WordPress dependencies - */ -import { - __experimentalNavigationGroup as NavigationGroup, - __experimentalNavigationMenu as NavigationMenu, -} from '@wordpress/components'; -import { __, _x } from '@wordpress/i18n'; - -/** - * Internal dependencies - */ -import TemplateNavigationItem from '../template-navigation-item'; -import { MENU_TEMPLATES, MENU_TEMPLATES_PAGES } from '../constants'; - -export default function TemplatesPagesMenu( { templates } ) { - const defaultTemplate = templates?.find( ( { slug } ) => slug === 'page' ); - const specificTemplates = - templates?.filter( ( { slug } ) => slug.startsWith( 'page-' ) ) ?? []; - - return ( - - - { map( specificTemplates, ( template ) => ( - - ) ) } - - - { defaultTemplate && ( - - - - ) } - - ); -} diff --git a/packages/edit-site/src/components/navigation-sidebar/navigation-panel/menus/templates-posts.js b/packages/edit-site/src/components/navigation-sidebar/navigation-panel/menus/templates-posts.js deleted file mode 100644 index 02225ee5bfb24..0000000000000 --- a/packages/edit-site/src/components/navigation-sidebar/navigation-panel/menus/templates-posts.js +++ /dev/null @@ -1,60 +0,0 @@ -/** - * External dependencies - */ -import { map } from 'lodash'; - -/** - * WordPress dependencies - */ -import { - __experimentalNavigationGroup as NavigationGroup, - __experimentalNavigationMenu as NavigationMenu, -} from '@wordpress/components'; -import { __, _x } from '@wordpress/i18n'; - -/** - * Internal dependencies - */ -import TemplateNavigationItem from '../template-navigation-item'; -import { - MENU_TEMPLATES, - MENU_TEMPLATES_POSTS, - TEMPLATES_POSTS, -} from '../constants'; - -export default function TemplatesPostsMenu( { templates } ) { - const generalTemplates = - templates?.filter( ( { slug } ) => TEMPLATES_POSTS.includes( slug ) ) ?? - []; - const specificTemplates = - templates?.filter( ( { slug } ) => slug.startsWith( 'post-' ) ) ?? []; - - return ( - - - { map( specificTemplates, ( template ) => ( - - ) ) } - - - - { map( generalTemplates, ( template ) => ( - - ) ) } - - - ); -} diff --git a/packages/edit-site/src/components/navigation-sidebar/navigation-panel/menus/templates-all.js b/packages/edit-site/src/components/navigation-sidebar/navigation-panel/menus/templates-sub.js similarity index 50% rename from packages/edit-site/src/components/navigation-sidebar/navigation-panel/menus/templates-all.js rename to packages/edit-site/src/components/navigation-sidebar/navigation-panel/menus/templates-sub.js index 934a35bd0e0f5..9d6322c25a4f8 100644 --- a/packages/edit-site/src/components/navigation-sidebar/navigation-panel/menus/templates-all.js +++ b/packages/edit-site/src/components/navigation-sidebar/navigation-panel/menus/templates-sub.js @@ -7,22 +7,31 @@ import { map } from 'lodash'; * WordPress dependencies */ import { __experimentalNavigationMenu as NavigationMenu } from '@wordpress/components'; -import { __ } from '@wordpress/i18n'; +import { useMemo } from '@wordpress/element'; /** * Internal dependencies */ import TemplateNavigationItem from '../template-navigation-item'; -import { MENU_TEMPLATES, MENU_TEMPLATES_ALL } from '../constants'; +import { MENU_TEMPLATES } from '../constants'; + +export default function TemplatesSubMenu( { menu, title, templates } ) { + const templatesFiltered = useMemo( + () => + templates + ?.filter( ( { location } ) => location === menu ) + ?.map( ( { template } ) => template ) ?? [], + [ menu, templates ] + ); -export default function TemplatesAllMenu( { templates } ) { return ( - { map( templates, ( template ) => ( + { map( templatesFiltered, ( template ) => ( + slug.startsWith( prefix ) + ); + if ( isPostsTemplate ) { + return MENU_TEMPLATES_POSTS; + } + + const isPagesTemplate = TEMPLATES_PAGES_PREFIXES.some( ( prefix ) => + slug.startsWith( prefix ) + ); + if ( isPagesTemplate ) { + return MENU_TEMPLATES_PAGES; + } + + return MENU_TEMPLATES_GENERAL; +} + +function getUnusedTemplates( templates, showOnFront ) { + const unusedTemplates = []; + + const templateSlugs = map( templates, 'slug' ); + const supersededTemplates = templates.filter( ( { slug } ) => + isTemplateSuperseded( slug, templateSlugs, showOnFront ) + ); + + return [ ...supersededTemplates, ...unusedTemplates ]; +} + +function getTemplatesLocationMap( templates ) { + return templates.reduce( ( obj, template ) => { + obj[ template.slug ] = getTemplateLocation( template ); + return obj; + }, {} ); +} export default function TemplatesMenu() { const [ search, setSearch ] = useState( '' ); @@ -38,14 +90,38 @@ export default function TemplatesMenu() { setSearch( value ); } ); - const templates = useSelect( - ( select ) => - select( 'core' ).getEntityRecords( 'postType', 'wp_template' ), - [] - ); + const { templates, showOnFront } = useSelect( ( select ) => { + const { getEntityRecords, getEditedEntityRecord } = select( coreStore ); + return { + templates: getEntityRecords( 'postType', 'wp_template', { + per_page: -1, + } ), + showOnFront: getEditedEntityRecord( 'root', 'site' ).show_on_front, + }; + }, [] ); + + const templatesWithLocation = useMemo( () => { + if ( ! templates ) { + return null; + } - const generalTemplates = templates?.filter( ( { slug } ) => - TEMPLATES_GENERAL.includes( slug ) + const unusedTemplates = getUnusedTemplates( templates, showOnFront ); + const templateLocations = getTemplatesLocationMap( templates ); + + return templates.map( ( template ) => ( { + template, + location: find( unusedTemplates, { slug: template.slug } ) + ? MENU_TEMPLATES_UNUSED + : templateLocations[ template.slug ], + } ) ); + }, [ templates ] ); + + const topLevelTemplates = useMemo( + () => + templatesWithLocation + ?.filter( ( { location } ) => location === MENU_TEMPLATES ) + ?.map( ( { template } ) => template ) ?? [], + [ templatesWithLocation ] ); return ( @@ -64,26 +140,32 @@ export default function TemplatesMenu() { { ! search && ( <> + { map( topLevelTemplates, ( template ) => ( + + ) ) } + - { map( generalTemplates, ( template ) => ( - - ) ) } ) } @@ -91,9 +173,26 @@ export default function TemplatesMenu() { ) } - - - + + + + ); } diff --git a/packages/edit-site/src/components/navigation-sidebar/navigation-panel/template-hierarchy.js b/packages/edit-site/src/components/navigation-sidebar/navigation-panel/template-hierarchy.js new file mode 100644 index 0000000000000..29dba28b9b5aa --- /dev/null +++ b/packages/edit-site/src/components/navigation-sidebar/navigation-panel/template-hierarchy.js @@ -0,0 +1,23 @@ +const TEMPLATE_OVERRIDES = { + singular: [ 'single', 'page' ], + index: [ 'archive', '404', 'search', 'singular', 'home' ], + home: [ 'front-page' ], +}; + +export function isTemplateSuperseded( slug, existingSlugs, showOnFront ) { + if ( ! TEMPLATE_OVERRIDES[ slug ] ) { + return false; + } + + // `home` template is unused if it is superseded by `front-page` + // or "show on front" is set to show a page rather than blog posts. + if ( slug === 'home' && showOnFront !== 'posts' ) { + return true; + } + + return TEMPLATE_OVERRIDES[ slug ].every( + ( overrideSlug ) => + existingSlugs.includes( overrideSlug ) || + isTemplateSuperseded( overrideSlug, existingSlugs, showOnFront ) + ); +}