From 3aeb89e6fdb8a72741ea9078f199f8265ffb8592 Mon Sep 17 00:00:00 2001 From: Jacopo Tomasone Date: Fri, 20 Nov 2020 12:43:18 +0000 Subject: [PATCH] Full Site Editing: Add Support for Templates Default and Custom Titles and Descriptions (JS Side) (#27038) - New templates created from the Site Editor sidebar will also automatically use the default template types definitions, and will be saved as `draft`. They will become `published` once saved the first time. - The default definitions are used across the Site Editor as a fallback for templates without title or excerpt. - Converted the `getTemplateInfo` utility into the `getTemplateInfo` selector, since it relies on the `core/editor` state. --- .../edit-site/src/components/editor/index.js | 21 +++ .../edit-site/src/components/header/index.js | 66 ++++--- .../navigation-panel/constants.js | 2 + .../navigation-panel/menus/templates.js | 3 +- .../navigation-panel/new-template-dropdown.js | 35 ++-- .../navigation-panel/style.scss | 5 + .../template-navigation-item.js | 20 ++- .../src/components/template-details/index.js | 11 +- .../utils/get-closest-available-template.js | 32 ++-- .../src/utils/get-template-info/constants.js | 57 ------ .../src/utils/get-template-info/index.js | 34 ---- .../test/__snapshots__/index.js.snap | 22 --- .../src/utils/get-template-info/test/index.js | 71 -------- packages/edit-site/src/utils/index.js | 1 - .../entity-record-item.js | 23 ++- packages/editor/src/store/selectors.js | 68 +++++++- packages/editor/src/store/test/selectors.js | 165 ++++++++++++++++++ 17 files changed, 387 insertions(+), 249 deletions(-) delete mode 100644 packages/edit-site/src/utils/get-template-info/constants.js delete mode 100644 packages/edit-site/src/utils/get-template-info/index.js delete mode 100644 packages/edit-site/src/utils/get-template-info/test/__snapshots__/index.js.snap delete mode 100644 packages/edit-site/src/utils/get-template-info/test/index.js diff --git a/packages/edit-site/src/components/editor/index.js b/packages/edit-site/src/components/editor/index.js index e950ec8c79d3c5..137a7206916ebe 100644 --- a/packages/edit-site/src/components/editor/index.js +++ b/packages/edit-site/src/components/editor/index.js @@ -105,8 +105,18 @@ function Editor() { }; }, [] ); const { editEntityRecord } = useDispatch( 'core' ); + const { updateEditorSettings } = useDispatch( 'core/editor' ); const { setPage, setIsInserterOpened } = useDispatch( 'core/edit-site' ); + // Keep the defaultTemplateTypes in the core/editor settings too, + // so that they can be selected with core/editor selectors in any editor. + // This is needed because edit-site doesn't initialize with EditorProvider, + // which internally uses updateEditorSettings as well. + const { defaultTemplateTypes } = settings; + useEffect( () => { + updateEditorSettings( { defaultTemplateTypes } ); + }, [ defaultTemplateTypes ] ); + const inlineStyles = useResizeCanvas( deviceType ); const [ @@ -121,9 +131,20 @@ function Editor() { ( entitiesToSave ) => { if ( entitiesToSave ) { const { getEditedEntityRecord } = select( 'core' ); + const { + __experimentalGetTemplateInfo: getTemplateInfo, + } = select( 'core/editor' ); entitiesToSave.forEach( ( { kind, name, key } ) => { const record = getEditedEntityRecord( kind, name, key ); + if ( 'postType' === kind && name === 'wp_template' ) { + const { title } = getTemplateInfo( record ); + return editEntityRecord( kind, name, key, { + status: 'publish', + title, + } ); + } + const edits = record.slug ? { status: 'publish', title: record.slug } : { status: 'publish' }; diff --git a/packages/edit-site/src/components/header/index.js b/packages/edit-site/src/components/header/index.js index 00c26276700f32..2afaf384d962d3 100644 --- a/packages/edit-site/src/components/header/index.js +++ b/packages/edit-site/src/components/header/index.js @@ -23,14 +23,13 @@ import UndoButton from './undo-redo/undo'; import RedoButton from './undo-redo/redo'; import DocumentActions from './document-actions'; import TemplateDetails from '../template-details'; -import { getTemplateInfo } from '../../utils'; export default function Header( { openEntitiesSavedStates } ) { const { deviceType, + entityTitle, hasFixedToolbar, template, - templatePart, templateType, isInserterOpen, } = useSelect( ( select ) => { @@ -43,20 +42,33 @@ export default function Header( { openEntitiesSavedStates } ) { isInserterOpened, } = select( 'core/edit-site' ); const { getEntityRecord } = select( 'core' ); + const { __experimentalGetTemplateInfo: getTemplateInfo } = select( + 'core/editor' + ); - const templatePartId = getTemplatePartId(); - const templateId = getTemplateId(); + const _templateType = getTemplateType(); + const _template = getEntityRecord( + 'postType', + 'wp_template', + getTemplateId() + ); + const _templatePart = getEntityRecord( + 'postType', + 'wp_template_part', + getTemplatePartId() + ); + + const _entityTitle = + 'wp_template' === _templateType + ? getTemplateInfo( _template ).title + : _templatePart?.slug; return { deviceType: __experimentalGetPreviewDeviceType(), + entityTitle: _entityTitle, hasFixedToolbar: isFeatureActive( 'fixedToolbar' ), - template: getEntityRecord( 'postType', 'wp_template', templateId ), - templatePart: getEntityRecord( - 'postType', - 'wp_template_part', - templatePartId - ), - templateType: getTemplateType(), + template: _template, + templateType: _templateType, isInserterOpen: isInserterOpened(), }; }, [] ); @@ -70,11 +82,6 @@ export default function Header( { openEntitiesSavedStates } ) { const displayBlockToolbar = ! isLargeViewport || deviceType !== 'Desktop' || hasFixedToolbar; - let { title } = getTemplateInfo( template ); - if ( 'wp_template_part' === templateType ) { - title = templatePart?.slug; - } - return (
@@ -109,22 +116,25 @@ export default function Header( { openEntitiesSavedStates } ) {
- - { templateType === 'wp_template' && - ( ( { onClose } ) => ( + { 'wp_template' === templateType && ( + + { ( { onClose } ) => ( - ) ) } - + ) } + + ) } + { 'wp_template_part' === templateType && ( + + ) }
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 3d6425735db8b8..5738942e4779d9 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 @@ -9,6 +9,8 @@ export const TEMPLATES_GENERAL = [ export const TEMPLATES_POSTS = [ 'home', 'single' ]; +export const TEMPLATES_STATUSES = [ 'publish', 'draft', 'auto-draft' ]; + export const MENU_ROOT = 'root'; export const MENU_CONTENT_CATEGORIES = 'content-categories'; export const MENU_CONTENT_PAGES = 'content-pages'; diff --git a/packages/edit-site/src/components/navigation-sidebar/navigation-panel/menus/templates.js b/packages/edit-site/src/components/navigation-sidebar/navigation-panel/menus/templates.js index 719aa9294fb9c2..f1f188b6d7a3b8 100644 --- a/packages/edit-site/src/components/navigation-sidebar/navigation-panel/menus/templates.js +++ b/packages/edit-site/src/components/navigation-sidebar/navigation-panel/menus/templates.js @@ -25,6 +25,7 @@ import { MENU_TEMPLATES_PAGES, MENU_TEMPLATES_POSTS, TEMPLATES_GENERAL, + TEMPLATES_STATUSES, } from '../constants'; import TemplatesAllMenu from './templates-all'; import NewTemplateDropdown from '../new-template-dropdown'; @@ -34,7 +35,7 @@ export default function TemplatesMenu() { const templates = useSelect( ( select ) => select( 'core' ).getEntityRecords( 'postType', 'wp_template', { - status: [ 'publish', 'auto-draft' ], + status: TEMPLATES_STATUSES, per_page: -1, } ), [] diff --git a/packages/edit-site/src/components/navigation-sidebar/navigation-panel/new-template-dropdown.js b/packages/edit-site/src/components/navigation-sidebar/navigation-panel/new-template-dropdown.js index 1248d2948d9d63..88903d2d2d40cb 100644 --- a/packages/edit-site/src/components/navigation-sidebar/navigation-panel/new-template-dropdown.js +++ b/packages/edit-site/src/components/navigation-sidebar/navigation-panel/new-template-dropdown.js @@ -1,7 +1,7 @@ /** * External dependencies */ -import { map, filter, includes } from 'lodash'; +import { filter, find, includes, map } from 'lodash'; /** * WordPress dependencies @@ -20,17 +20,23 @@ import { Icon, plus } from '@wordpress/icons'; * Internal dependencies */ import getClosestAvailableTemplate from '../../../utils/get-closest-available-template'; -import { TEMPLATES_DEFAULT_DETAILS } from '../../../utils/get-template-info/constants'; +import { TEMPLATES_STATUSES } from './constants'; export default function NewTemplateDropdown() { - const templates = useSelect( - ( select ) => - select( 'core' ).getEntityRecords( 'postType', 'wp_template', { - status: [ 'publish', 'auto-draft' ], - per_page: -1, - } ), - [] - ); + const { defaultTemplateTypes, templates } = useSelect( ( select ) => { + const { + __experimentalGetDefaultTemplateTypes: getDefaultTemplateTypes, + } = select( 'core/editor' ); + const templateEntities = select( 'core' ).getEntityRecords( + 'postType', + 'wp_template', + { status: TEMPLATES_STATUSES, per_page: -1 } + ); + return { + defaultTemplateTypes: getDefaultTemplateTypes(), + templates: templateEntities, + }; + }, [] ); const { addTemplate } = useDispatch( 'core/edit-site' ); const createTemplate = ( slug ) => { @@ -38,18 +44,21 @@ export default function NewTemplateDropdown() { slug, templates ); + const { title, description } = find( defaultTemplateTypes, { slug } ); addTemplate( { content: closestAvailableTemplate.content.raw, - slug, - title: slug, + excerpt: description, + // Slugs need to be strings, so this is for template `404` + slug: slug.toString(), status: 'draft', + title, } ); }; const existingTemplateSlugs = map( templates, 'slug' ); const missingTemplates = filter( - TEMPLATES_DEFAULT_DETAILS, + defaultTemplateTypes, ( template ) => ! includes( existingTemplateSlugs, template.slug ) ); diff --git a/packages/edit-site/src/components/navigation-sidebar/navigation-panel/style.scss b/packages/edit-site/src/components/navigation-sidebar/navigation-panel/style.scss index 4d47e998b98eb8..c4b53d124fd426 100644 --- a/packages/edit-site/src/components/navigation-sidebar/navigation-panel/style.scss +++ b/packages/edit-site/src/components/navigation-sidebar/navigation-panel/style.scss @@ -102,6 +102,11 @@ } } +.edit-site-navigation-panel__template-item-title { + em { + margin-right: 1ch; + } +} .edit-site-navigation-panel__template-item-description { padding-top: $grid-unit-05; font-size: 12px; diff --git a/packages/edit-site/src/components/navigation-sidebar/navigation-panel/template-navigation-item.js b/packages/edit-site/src/components/navigation-sidebar/navigation-panel/template-navigation-item.js index 507c9c4d15a80d..b63fc942368b9c 100644 --- a/packages/edit-site/src/components/navigation-sidebar/navigation-panel/template-navigation-item.js +++ b/packages/edit-site/src/components/navigation-sidebar/navigation-panel/template-navigation-item.js @@ -5,21 +5,30 @@ import { Button, __experimentalNavigationItem as NavigationItem, } from '@wordpress/components'; -import { useDispatch } from '@wordpress/data'; +import { useDispatch, useSelect } from '@wordpress/data'; import { useState } from '@wordpress/element'; +import { __ } from '@wordpress/i18n'; /** * Internal dependencies */ import TemplatePreview from './template-preview'; import { NavigationPanelPreviewFill } from '../index'; -import { getTemplateInfo } from '../../../utils'; export default function TemplateNavigationItem( { item } ) { + const { title, description } = useSelect( + ( select ) => + 'wp_template' === item.type + ? select( 'core/editor' ).__experimentalGetTemplateInfo( item ) + : { title: item?.slug, description: '' }, + [] + ); const { setTemplate, setTemplatePart } = useDispatch( 'core/edit-site' ); const [ isPreviewVisible, setIsPreviewVisible ] = useState( false ); - const { title, description } = getTemplateInfo( item ); + if ( ! item ) { + return null; + } const onActivateItem = () => 'wp_template' === item.type @@ -37,7 +46,10 @@ export default function TemplateNavigationItem( { item } ) { onMouseEnter={ () => setIsPreviewVisible( true ) } onMouseLeave={ () => setIsPreviewVisible( false ) } > - { title } +
+ { 'draft' === item.status && { __( '[Draft]' ) } } + { title } +
{ description && (
{ description } diff --git a/packages/edit-site/src/components/template-details/index.js b/packages/edit-site/src/components/template-details/index.js index a8bcee06279078..ae2d1ab4f01577 100644 --- a/packages/edit-site/src/components/template-details/index.js +++ b/packages/edit-site/src/components/template-details/index.js @@ -3,22 +3,25 @@ */ import { __, sprintf } from '@wordpress/i18n'; import { Button, __experimentalText as Text } from '@wordpress/components'; -import { useDispatch } from '@wordpress/data'; +import { useDispatch, useSelect } from '@wordpress/data'; /** * Internal dependencies */ -import { getTemplateInfo } from '../../utils'; import { MENU_TEMPLATES } from '../navigation-sidebar/navigation-panel/constants'; export default function TemplateDetails( { template, onClose } ) { + const { title, description } = useSelect( + ( select ) => + select( 'core/editor' ).__experimentalGetTemplateInfo( template ), + [] + ); const { openNavigationPanelToMenu } = useDispatch( 'core/edit-site' ); + if ( ! template ) { return null; } - const { title, description } = getTemplateInfo( template ); - const showTemplateInSidebar = () => { onClose(); openNavigationPanelToMenu( MENU_TEMPLATES ); diff --git a/packages/edit-site/src/utils/get-closest-available-template.js b/packages/edit-site/src/utils/get-closest-available-template.js index a12bbbe96d944e..e73be0204d753a 100644 --- a/packages/edit-site/src/utils/get-closest-available-template.js +++ b/packages/edit-site/src/utils/get-closest-available-template.js @@ -4,20 +4,28 @@ import { find } from 'lodash'; export default function getClosestAvailableTemplate( slug, templates ) { - if ( 'front-page' === slug ) { - const homeTemplate = find( templates, { slug: 'home' } ); - if ( homeTemplate ) { - return homeTemplate; - } + const template = find( templates, { slug } ); + if ( template ) { + return template; } - if ( 'single' === slug || 'page' === slug ) { - const singularTemplate = find( templates, { slug: 'singular' } ); - if ( singularTemplate ) { - return singularTemplate; - } + switch ( slug ) { + case 'single': + case 'page': + return getClosestAvailableTemplate( 'singular', templates ); + case 'author': + case 'category': + case 'taxonomy': + case 'date': + case 'tag': + return getClosestAvailableTemplate( 'archive', templates ); + case 'front-page': + return getClosestAvailableTemplate( 'home', templates ); + case 'attachment': + return getClosestAvailableTemplate( 'single', templates ); + case 'privacy-policy': + return getClosestAvailableTemplate( 'page', templates ); } - const indexTemplate = find( templates, { slug: 'index' } ); - return indexTemplate; + return find( templates, { slug: 'index' } ); } diff --git a/packages/edit-site/src/utils/get-template-info/constants.js b/packages/edit-site/src/utils/get-template-info/constants.js deleted file mode 100644 index 0b0ccee1ecd262..00000000000000 --- a/packages/edit-site/src/utils/get-template-info/constants.js +++ /dev/null @@ -1,57 +0,0 @@ -/** - * WordPress dependencies - */ -import { __, _x } from '@wordpress/i18n'; - -/** - * Default template details, ordered by perceived importance - */ -export const TEMPLATES_DEFAULT_DETAILS = [ - { - slug: 'front-page', - title: _x( 'Front Page', 'template name' ), - description: __( - 'Front page template, whether it displays the blog posts index or a static page' - ), - }, - { - slug: 'index', - title: _x( 'Index', 'template name' ), - description: __( 'Default template' ), - }, - { - slug: 'home', - title: __( 'Home Page' ), - description: __( 'Template for the latest blog posts' ), - }, - { - slug: 'page', - title: __( 'Single Page' ), - description: __( 'Template for single pages' ), - }, - { - slug: 'singular', - title: _x( 'Singular', 'template name' ), - description: __( 'Default template for both single posts and pages' ), - }, - { - slug: 'single', - title: __( 'Single Post' ), - description: __( 'Template for single posts' ), - }, - { - slug: 'archive', - title: _x( 'Archive', 'template name' ), - description: __( 'Generic archive template' ), - }, - { - slug: 'search', - title: _x( 'Search Results', 'template name' ), - description: __( 'Search results template' ), - }, - { - slug: '404', - title: _x( '404 (Not Found)', 'template name' ), - description: __( 'Template for "not found" errors' ), - }, -]; diff --git a/packages/edit-site/src/utils/get-template-info/index.js b/packages/edit-site/src/utils/get-template-info/index.js deleted file mode 100644 index b397237d65831a..00000000000000 --- a/packages/edit-site/src/utils/get-template-info/index.js +++ /dev/null @@ -1,34 +0,0 @@ -/** - * External dependencies - */ -import { find } from 'lodash'; - -/** - * Internal dependencies - */ -import { TEMPLATES_DEFAULT_DETAILS } from './constants'; - -/** - * Given a template entity, return information about it which is ready to be - * rendered, such as the title and description. - * - * @param {Object} template The template for which we need information. - * @return {Object} Information about the template, including title and description. - */ -export default function getTemplateInfo( template ) { - if ( ! template ) { - return {}; - } - const { title: defaultTitle, description: defaultDescription } = - find( TEMPLATES_DEFAULT_DETAILS, { slug: template?.slug } ) ?? {}; - - let title = template?.title?.rendered ?? template.slug; - if ( title !== template.slug ) { - title = template.title.rendered; - } else if ( defaultTitle ) { - title = defaultTitle; - } - - const description = template?.excerpt?.rendered || defaultDescription; - return { title, description }; -} diff --git a/packages/edit-site/src/utils/get-template-info/test/__snapshots__/index.js.snap b/packages/edit-site/src/utils/get-template-info/test/__snapshots__/index.js.snap deleted file mode 100644 index 10003587248ca2..00000000000000 --- a/packages/edit-site/src/utils/get-template-info/test/__snapshots__/index.js.snap +++ /dev/null @@ -1,22 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`get template info should return both a title and a description 1`] = ` -Object { - "description": "test description", - "title": "Index", -} -`; - -exports[`get template info should return both a title and a description 2`] = ` -Object { - "description": "Default template", - "title": "Index", -} -`; - -exports[`get template info should return both a title and a description 3`] = ` -Object { - "description": undefined, - "title": undefined, -} -`; diff --git a/packages/edit-site/src/utils/get-template-info/test/index.js b/packages/edit-site/src/utils/get-template-info/test/index.js deleted file mode 100644 index dbf0e9450d5574..00000000000000 --- a/packages/edit-site/src/utils/get-template-info/test/index.js +++ /dev/null @@ -1,71 +0,0 @@ -/** - * Internal dependencies - */ -import getTemplateInfo from '../'; - -describe( 'get template info', () => { - it( 'should return an empty object if no template is passed', () => { - expect( getTemplateInfo( null ) ).toEqual( {} ); - expect( getTemplateInfo( undefined ) ).toEqual( {} ); - expect( getTemplateInfo( false ) ).toEqual( {} ); - } ); - - it( 'should return the default title if none is defined on the template', () => { - expect( getTemplateInfo( { slug: 'index' } ).title ).toEqual( 'Index' ); - } ); - - it( 'should return the rendered title if one is defined on the template', () => { - expect( - getTemplateInfo( { - slug: 'index', - title: { rendered: 'test title' }, - } ).title - ).toEqual( 'test title' ); - } ); - - it( 'should return the slug if no title is found', () => { - expect( - getTemplateInfo( { - slug: 'not a real template', - } ).title - ).toEqual( 'not a real template' ); - } ); - - it( 'should return the default description if none is defined on the template', () => { - expect( - getTemplateInfo( { - slug: 'index', - } ).description - ).toEqual( 'Default template' ); - } ); - - it( 'should return the rendered excerpt as description if defined on the template', () => { - expect( - getTemplateInfo( { - slug: 'index', - excerpt: { - rendered: 'test description', - }, - } ).description - ).toEqual( 'test description' ); - } ); - - it( 'should return both a title and a description', () => { - expect( - getTemplateInfo( { - slug: 'index', - excerpt: { - rendered: 'test description', - }, - } ) - ).toMatchSnapshot(); - - expect( - getTemplateInfo( { - slug: 'index', - } ) - ).toMatchSnapshot(); - - expect( getTemplateInfo( {} ) ).toMatchSnapshot(); - } ); -} ); diff --git a/packages/edit-site/src/utils/index.js b/packages/edit-site/src/utils/index.js index 81512be7171961..1fc2fc98df0855 100644 --- a/packages/edit-site/src/utils/index.js +++ b/packages/edit-site/src/utils/index.js @@ -1,2 +1 @@ export { default as findTemplate } from './find-template'; -export { default as getTemplateInfo } from './get-template-info'; diff --git a/packages/editor/src/components/entities-saved-states/entity-record-item.js b/packages/editor/src/components/entities-saved-states/entity-record-item.js index 44afeb672a5edb..996c9892200570 100644 --- a/packages/editor/src/components/entities-saved-states/entity-record-item.js +++ b/packages/editor/src/components/entities-saved-states/entity-record-item.js @@ -28,6 +28,25 @@ export default function EntityRecordItem( { return parents[ parents.length - 1 ]; }, [] ); + // Handle templates that might use default descriptive titles + const entityRecordTitle = useSelect( + ( select ) => { + if ( 'postType' !== kind || 'wp_template' !== name ) { + return title; + } + + const template = select( 'core' ).getEditedEntityRecord( + kind, + name, + key + ); + return select( 'core/editor' ).__experimentalGetTemplateInfo( + template + ).title; + }, + [ name, kind, title, key ] + ); + const isSelected = useSelect( ( select ) => { const selectedBlockId = select( @@ -50,7 +69,9 @@ export default function EntityRecordItem( { return ( { title || __( 'Untitled' ) } } + label={ + { entityRecordTitle || __( 'Untitled' ) } + } checked={ checked } onChange={ onChange } /> diff --git a/packages/editor/src/store/selectors.js b/packages/editor/src/store/selectors.js index c4394cb0db56a9..337d560998c5cf 100644 --- a/packages/editor/src/store/selectors.js +++ b/packages/editor/src/store/selectors.js @@ -1,7 +1,17 @@ /** * External dependencies */ -import { find, get, has, pick, mapValues, includes, some } from 'lodash'; +import { + find, + get, + has, + isString, + pick, + mapValues, + includes, + some, +} from 'lodash'; +import createSelector from 'rememo'; /** * WordPress dependencies @@ -1650,3 +1660,59 @@ export const hasInserterItems = getBlockEditorSelector( 'hasInserterItems' ); export const getBlockListSettings = getBlockEditorSelector( 'getBlockListSettings' ); + +/** + * Returns the default template types. + * + * @param {Object} state Global application state. + * + * @return {Object} The template types. + */ +export function __experimentalGetDefaultTemplateTypes( state ) { + return getEditorSettings( state )?.defaultTemplateTypes; +} + +/** + * Returns a default template type searched by slug. + * + * @param {Object} state Global application state. + * @param {string} slug The template type slug. + * + * @return {Object} The template type. + */ +export const __experimentalGetDefaultTemplateType = createSelector( + ( state, slug ) => + find( __experimentalGetDefaultTemplateTypes( state ), { slug } ) || {}, + ( state, slug ) => [ __experimentalGetDefaultTemplateTypes( state ), slug ] +); + +/** + * Given a template entity, return information about it which is ready to be + * rendered, such as the title and description. + * + * @param {Object} state Global application state. + * @param {Object} template The template for which we need information. + * @return {Object} Information about the template, including title and description. + */ +export function __experimentalGetTemplateInfo( state, template ) { + if ( ! template ) { + return {}; + } + + const { excerpt, slug, title } = template; + const { + title: defaultTitle, + description: defaultDescription, + } = __experimentalGetDefaultTemplateType( state, slug ); + + const templateTitle = isString( title ) ? title : title?.rendered; + const templateDescription = isString( excerpt ) ? excerpt : excerpt?.raw; + + return { + title: + templateTitle && templateTitle !== slug + ? templateTitle + : defaultTitle || slug, + description: templateDescription || defaultDescription, + }; +} diff --git a/packages/editor/src/store/test/selectors.js b/packages/editor/src/store/test/selectors.js index 5746263abd2920..d7ef6c5de3293b 100644 --- a/packages/editor/src/store/test/selectors.js +++ b/packages/editor/src/store/test/selectors.js @@ -170,8 +170,24 @@ const { isPostSavingLocked, isPostAutosavingLocked, canUserUseUnfilteredHTML, + __experimentalGetDefaultTemplateType, + __experimentalGetDefaultTemplateTypes, + __experimentalGetTemplateInfo, } = selectors; +const defaultTemplateTypes = [ + { + title: 'Default (Index)', + description: 'Main template', + slug: 'index', + }, + { + title: '404 (Not Found)', + description: 'Applied when content cannot be found', + slug: '404', + }, +]; + describe( 'selectors', () => { let cachedSelectors; @@ -2911,4 +2927,153 @@ describe( 'selectors', () => { expect( canUserUseUnfilteredHTML( state ) ).toBe( false ); } ); } ); + + describe( '__experimentalGetDefaultTemplateTypes', () => { + const state = { editorSettings: { defaultTemplateTypes } }; + + it( 'returns undefined if there are no default template types', () => { + const emptyState = { editorSettings: {} }; + expect( + __experimentalGetDefaultTemplateTypes( emptyState ) + ).toBeUndefined(); + } ); + + it( 'returns a list of default template types if present in state', () => { + expect( + __experimentalGetDefaultTemplateTypes( state ) + ).toHaveLength( 2 ); + } ); + } ); + + describe( '__experimentalGetDefaultTemplateType', () => { + const state = { editorSettings: { defaultTemplateTypes } }; + + it( 'returns an empty object if there are no default template types', () => { + const emptyState = { editorSettings: {} }; + expect( + __experimentalGetDefaultTemplateType( emptyState, 'slug' ) + ).toEqual( {} ); + } ); + + it( 'returns an empty object if the requested slug is not found', () => { + expect( + __experimentalGetDefaultTemplateType( state, 'foobar' ) + ).toEqual( {} ); + } ); + + it( 'returns the requested default template type ', () => { + expect( + __experimentalGetDefaultTemplateType( state, 'index' ) + ).toEqual( { + title: 'Default (Index)', + description: 'Main template', + slug: 'index', + } ); + } ); + + it( 'returns the requested default template type even when the slug is numeric', () => { + expect( + __experimentalGetDefaultTemplateType( state, '404' ) + ).toEqual( { + title: '404 (Not Found)', + description: 'Applied when content cannot be found', + slug: '404', + } ); + } ); + } ); + + describe( '__experimentalGetTemplateInfo', () => { + const state = { editorSettings: { defaultTemplateTypes } }; + + it( 'should return an empty object if no template is passed', () => { + expect( __experimentalGetTemplateInfo( state, null ) ).toEqual( + {} + ); + expect( __experimentalGetTemplateInfo( state, undefined ) ).toEqual( + {} + ); + expect( __experimentalGetTemplateInfo( state, false ) ).toEqual( + {} + ); + } ); + + it( 'should return the default title if none is defined on the template', () => { + expect( + __experimentalGetTemplateInfo( state, { slug: 'index' } ).title + ).toEqual( 'Default (Index)' ); + } ); + + it( 'should return the rendered title if defined on the template', () => { + expect( + __experimentalGetTemplateInfo( state, { + slug: 'index', + title: { rendered: 'test title' }, + } ).title + ).toEqual( 'test title' ); + } ); + + it( 'should return the slug if no title is found', () => { + expect( + __experimentalGetTemplateInfo( state, { + slug: 'not a real template', + } ).title + ).toEqual( 'not a real template' ); + } ); + + it( 'should return the default description if none is defined on the template', () => { + expect( + __experimentalGetTemplateInfo( state, { slug: 'index' } ) + .description + ).toEqual( 'Main template' ); + } ); + + it( 'should return the raw excerpt as description if defined on the template', () => { + expect( + __experimentalGetTemplateInfo( state, { + slug: 'index', + excerpt: { raw: 'test description' }, + } ).description + ).toEqual( 'test description' ); + } ); + + it( 'should return both a title and a description', () => { + expect( + __experimentalGetTemplateInfo( state, { slug: 'index' } ) + ).toEqual( { + title: 'Default (Index)', + description: 'Main template', + } ); + + expect( + __experimentalGetTemplateInfo( state, { + slug: 'index', + title: { rendered: 'test title' }, + } ) + ).toEqual( { + title: 'test title', + description: 'Main template', + } ); + + expect( + __experimentalGetTemplateInfo( state, { + slug: 'index', + excerpt: { raw: 'test description' }, + } ) + ).toEqual( { + title: 'Default (Index)', + description: 'test description', + } ); + + expect( + __experimentalGetTemplateInfo( state, { + slug: 'index', + title: { rendered: 'test title' }, + excerpt: { raw: 'test description' }, + } ) + ).toEqual( { + title: 'test title', + description: 'test description', + } ); + } ); + } ); } );