diff --git a/packages/edit-site/src/components/add-new-template/add-custom-generic-template-modal-content.js b/packages/edit-site/src/components/add-new-template/add-custom-generic-template-modal-content.js new file mode 100644 index 0000000000000..6da96e791679b --- /dev/null +++ b/packages/edit-site/src/components/add-new-template/add-custom-generic-template-modal-content.js @@ -0,0 +1,82 @@ +/** + * External dependencies + */ +import { kebabCase } from 'lodash'; + +/** + * WordPress dependencies + */ +import { useState } from '@wordpress/element'; +import { __ } from '@wordpress/i18n'; +import { + Button, + TextControl, + __experimentalHStack as HStack, + __experimentalVStack as VStack, +} from '@wordpress/components'; + +function AddCustomGenericTemplateModalContent( { onClose, createTemplate } ) { + const [ title, setTitle ] = useState( '' ); + const defaultTitle = __( 'Custom Template' ); + const [ isBusy, setIsBusy ] = useState( false ); + async function onCreateTemplate( event ) { + event.preventDefault(); + if ( isBusy ) { + return; + } + setIsBusy( true ); + try { + await createTemplate( + { + slug: + 'wp-custom-template-' + + kebabCase( title || defaultTitle ), + title: title || defaultTitle, + }, + false + ); + } finally { + setIsBusy( false ); + } + } + return ( +
+ + + + + + + +
+ ); +} + +export default AddCustomGenericTemplateModalContent; diff --git a/packages/edit-site/src/components/add-new-template/add-custom-generic-template-modal.js b/packages/edit-site/src/components/add-new-template/add-custom-generic-template-modal.js deleted file mode 100644 index 05688d032a591..0000000000000 --- a/packages/edit-site/src/components/add-new-template/add-custom-generic-template-modal.js +++ /dev/null @@ -1,101 +0,0 @@ -/** - * External dependencies - */ -import { kebabCase } from 'lodash'; - -/** - * WordPress dependencies - */ -import { useState } from '@wordpress/element'; -import { __ } from '@wordpress/i18n'; -import { - Button, - Modal, - TextControl, - __experimentalHStack as HStack, - __experimentalVStack as VStack, -} from '@wordpress/components'; - -/** - * Internal dependencies - */ -import TemplateActionsLoadingScreen from './template-actions-loading-screen'; - -function AddCustomGenericTemplateModal( { - onClose, - createTemplate, - isCreatingTemplate, -} ) { - const [ title, setTitle ] = useState( '' ); - const defaultTitle = __( 'Custom Template' ); - const [ isBusy, setIsBusy ] = useState( false ); - async function onCreateTemplate( event ) { - event.preventDefault(); - if ( isBusy ) { - return; - } - setIsBusy( true ); - try { - await createTemplate( - { - slug: - 'wp-custom-template-' + - kebabCase( title || defaultTitle ), - title: title || defaultTitle, - }, - false - ); - } finally { - setIsBusy( false ); - } - } - return ( - { - onClose(); - } } - overlayClassName="edit-site-custom-generic-template__modal" - > - { isCreatingTemplate && } -
- - - - - - - -
-
- ); -} - -export default AddCustomGenericTemplateModal; diff --git a/packages/edit-site/src/components/add-new-template/add-custom-template-modal.js b/packages/edit-site/src/components/add-new-template/add-custom-template-modal-content.js similarity index 89% rename from packages/edit-site/src/components/add-new-template/add-custom-template-modal.js rename to packages/edit-site/src/components/add-new-template/add-custom-template-modal-content.js index ccab5d82bd058..5636ec16e1ac1 100644 --- a/packages/edit-site/src/components/add-new-template/add-custom-template-modal.js +++ b/packages/edit-site/src/components/add-new-template/add-custom-template-modal-content.js @@ -2,12 +2,11 @@ * WordPress dependencies */ import { useState, useMemo, useEffect } from '@wordpress/element'; -import { __, sprintf } from '@wordpress/i18n'; +import { __ } from '@wordpress/i18n'; import { Button, Flex, FlexItem, - Modal, SearchControl, TextHighlight, __experimentalText as Text, @@ -23,7 +22,6 @@ import { decodeEntities } from '@wordpress/html-entities'; /** * Internal dependencies */ -import TemplateActionsLoadingScreen from './template-actions-loading-screen'; import { mapToIHasNameAndId } from './utils'; const EMPTY_ARRAY = []; @@ -179,36 +177,25 @@ function SuggestionList( { entityForSuggestions, onSelect } ) { ); } -function AddCustomTemplateModal( { - onClose, - onSelect, - entityForSuggestions, - isCreatingTemplate, -} ) { +function AddCustomTemplateModalContent( { onSelect, entityForSuggestions } ) { const [ showSearchEntities, setShowSearchEntities ] = useState( entityForSuggestions.hasGeneralTemplate ); - const baseCssClass = 'edit-site-custom-template-modal'; return ( - - { isCreatingTemplate && } { ! showSearchEntities && ( - + <> { __( 'Select whether to create a single template for all items or a specific one.' ) } @@ -272,10 +259,10 @@ function AddCustomTemplateModal( { - + ) } { showSearchEntities && ( - + <> { __( 'This template will be used only for the specific item chosen.' @@ -285,10 +272,10 @@ function AddCustomTemplateModal( { entityForSuggestions={ entityForSuggestions } onSelect={ onSelect } /> - + ) } - + ); } -export default AddCustomTemplateModal; +export default AddCustomTemplateModalContent; diff --git a/packages/edit-site/src/components/add-new-template/new-template.js b/packages/edit-site/src/components/add-new-template/new-template.js index 3db38fffdee81..841d45749e628 100644 --- a/packages/edit-site/src/components/add-new-template/new-template.js +++ b/packages/edit-site/src/components/add-new-template/new-template.js @@ -1,34 +1,22 @@ +/** + * External dependencies + */ +import classnames from 'classnames'; + /** * WordPress dependencies */ import { - DropdownMenu, - MenuGroup, - MenuItem, - Tooltip, - VisuallyHidden, + Button, + Modal, + __experimentalGrid as Grid, + __experimentalText as Text, + __experimentalVStack as VStack, } from '@wordpress/components'; import { useState } from '@wordpress/element'; import { useDispatch } from '@wordpress/data'; import { store as coreStore } from '@wordpress/core-data'; -import { - archive, - blockMeta, - category, - home, - list, - media, - notFound, - page, - plus, - post, - postAuthor, - postDate, - postList, - search, - tag, - layout as customGenericTemplateIcon, -} from '@wordpress/icons'; +import { plus } from '@wordpress/icons'; import { __, sprintf } from '@wordpress/i18n'; import { store as noticesStore } from '@wordpress/notices'; import { privateApis as routerPrivateApis } from '@wordpress/router'; @@ -36,7 +24,7 @@ import { privateApis as routerPrivateApis } from '@wordpress/router'; /** * Internal dependencies */ -import AddCustomTemplateModal from './add-custom-template-modal'; +import AddCustomTemplateModalContent from './add-custom-template-modal-content'; import { useExistingTemplates, useDefaultTemplateTypes, @@ -45,7 +33,7 @@ import { useAuthorMenuItem, usePostTypeArchiveMenuItems, } from './utils'; -import AddCustomGenericTemplateModal from './add-custom-generic-template-modal'; +import AddCustomGenericTemplateModalContent from './add-custom-generic-template-modal-content'; import TemplateActionsLoadingScreen from './template-actions-loading-screen'; import { store as editSiteStore } from '../../store'; import { unlock } from '../../private-apis'; @@ -68,21 +56,35 @@ const DEFAULT_TEMPLATE_SLUGS = [ '404', ]; -const TEMPLATE_ICONS = { - 'front-page': home, - home: postList, - single: post, - page, - archive, - search, - 404: notFound, - index: list, - category, - author: postAuthor, - taxonomy: blockMeta, - date: postDate, - tag, - attachment: media, +function TemplateListItem( { title, description, onClick } ) { + return ( + + ); +} + +const modalContentMap = { + templatesList: 1, + customTemplate: 2, + customGenericTemplate: 3, }; export default function NewTemplate( { @@ -90,8 +92,10 @@ export default function NewTemplate( { toggleProps, showIcon = true, } ) { - const [ showCustomTemplateModal, setShowCustomTemplateModal ] = - useState( false ); + const [ showModal, setShowModal ] = useState( false ); + const [ modalContent, setModalContent ] = useState( + modalContentMap.templatesList + ); const [ showCustomGenericTemplateModal, setShowCustomGenericTemplateModal, @@ -159,130 +163,114 @@ export default function NewTemplate( { setIsCreatingTemplate( false ); } } + const onModalClose = () => { + setShowModal( false ); + setModalContent( modalContentMap.templatesList ); + }; - const missingTemplates = useMissingTemplates( - setEntityForSuggestions, - setShowCustomTemplateModal + const missingTemplates = useMissingTemplates( setEntityForSuggestions, () => + setModalContent( modalContentMap.customTemplate ) ); if ( ! missingTemplates.length ) { return null; } + const { as: Toggle = Button, ...restToggleProps } = toggleProps ?? {}; - const customTemplateDescription = __( - 'A custom template can be manually applied to any post or page.' - ); - + let modalTitle = __( 'Add template' ); + if ( modalContent === modalContentMap.customTemplate ) { + modalTitle = sprintf( + // translators: %s: Name of the post type e.g: "Post". + __( 'Add template: %s' ), + entityForSuggestions.labels.singular_name + ); + } else if ( showCustomGenericTemplateModal ) { + modalTitle = __( 'Create custom template' ); + } return ( <> - } + setShowModal( true ) } icon={ showIcon ? plus : null } - text={ showIcon ? null : postType.labels.add_new } label={ postType.labels.add_new_item } - popoverProps={ { - noArrow: false, - } } - toggleProps={ toggleProps } > - { () => ( - <> - { isCreatingTemplate && ( - - ) } -
- - { missingTemplates.map( ( template ) => { - const { - title, - description, - slug, - onClick, - icon, - } = template; - return ( - - - onClick - ? onClick( template ) - : createTemplate( - template - ) - } - > - { title } - { /* TODO: This probably won't be needed if the component is accessible. - * @see https://github.com/WordPress/gutenberg/issues/48222 */ } - - { description } - - - - ); - } ) } - - - - + { showModal && ( + + { modalContent === modalContentMap.templatesList && ( + + { missingTemplates.map( ( template ) => { + const { title, description, slug, onClick } = + template; + return ( + - setShowCustomGenericTemplateModal( - true - ) + onClick + ? onClick( template ) + : createTemplate( template ) } - > - { __( 'Custom template' ) } - { /* TODO: This probably won't be needed if the component is accessible. - * @see https://github.com/WordPress/gutenberg/issues/48222 */ } - - { customTemplateDescription } - - - - -
- - ) } -
- { showCustomTemplateModal && ( - setShowCustomTemplateModal( false ) } - onSelect={ createTemplate } - entityForSuggestions={ entityForSuggestions } - isCreatingTemplate={ isCreatingTemplate } - /> - ) } - { showCustomGenericTemplateModal && ( - setShowCustomGenericTemplateModal( false ) } - createTemplate={ createTemplate } - isCreatingTemplate={ isCreatingTemplate } - /> + /> + ); + } ) } + + setShowCustomGenericTemplateModal( true ) + } + /> + + ) } + { modalContent === modalContentMap.customTemplate && ( + + ) } + { modalContent === + modalContentMap.customGenericTemplate && ( + + ) } + ) } ); } -function useMissingTemplates( - setEntityForSuggestions, - setShowCustomTemplateModal -) { +function useMissingTemplates( setEntityForSuggestions, onClick ) { const existingTemplates = useExistingTemplates(); const defaultTemplateTypes = useDefaultTemplateTypes(); const existingTemplateSlugs = ( existingTemplates || [] ).map( @@ -294,7 +282,7 @@ function useMissingTemplates( ! existingTemplateSlugs.includes( template.slug ) ); const onClickMenuItem = ( _entityForSuggestions ) => { - setShowCustomTemplateModal( true ); + onClick?.(); setEntityForSuggestions( _entityForSuggestions ); }; // We need to replace existing default template types with diff --git a/packages/edit-site/src/components/add-new-template/style.scss b/packages/edit-site/src/components/add-new-template/style.scss index fc33da1f35a13..f0617ad62fc5b 100644 --- a/packages/edit-site/src/components/add-new-template/style.scss +++ b/packages/edit-site/src/components/add-new-template/style.scss @@ -1,70 +1,23 @@ -.edit-site-new-template-dropdown { - .edit-site-new-template-dropdown__menu-groups { - @include break-small() { - min-width: 300px; - } - } +.edit-site-custom-template-modal { + &__contents-wrapper { + height: 100%; + justify-content: flex-start !important; // Required as topLeft alignment isn't working on VStack - // The specificity is needed to override the default tooltip styles. - &__menu-item-tooltip.components-tooltip .components-popover__content { - max-width: 320px; - padding: $grid-unit-10 $grid-unit-15; - border-radius: 2px; - white-space: pre-wrap; - min-width: 0; - width: auto; - text-align: left; - } -} + > * { + width: 100%; + } -.edit-site-custom-template-modal { - &__suggestions_list { - margin-left: - $grid-unit-15; - margin-right: - $grid-unit-15; + &__suggestions_list { + margin-left: - $grid-unit-15; + margin-right: - $grid-unit-15; + width: calc(100% + #{$grid-unit-15 * 2}); + } } &__contents { > .components-button { - padding: $grid-unit-30; - border-radius: $radius-block-ui; height: auto; - display: flex; - flex-direction: column; justify-content: center; - border: $border-width solid $gray-300; - - // Show the boundary of the button, in High Contrast Mode. - outline: 1px solid transparent; - - span:first-child { - color: $gray-900; - } - - span { - color: $gray-700; - } - - &:hover { - color: var(--wp-admin-theme-color-darker-10); - background: rgba(var(--wp-admin-theme-color--rgb), 0.04); - border-color: transparent; - - span { - color: var(--wp-admin-theme-color); - } - } - - &:focus { - box-shadow: 0 0 0 var(--wp-admin-border-width-focus) var(--wp-admin-theme-color); - border-color: transparent; - - // Windows High Contrast mode will show this outline, but not the box-shadow. - outline: 3px solid transparent; - - span:first-child { - color: var(--wp-admin-theme-color); - } - } } } @@ -86,7 +39,6 @@ .edit-site-custom-template-modal__suggestions_list { @include break-small() { - height: 232px; overflow: scroll; } @@ -177,5 +129,72 @@ align-items: center; justify-content: center; height: 100%; + position: absolute; + left: 50%; + transform: translateX(-50%); + } +} + +.edit-site-add-new-template__modal { + max-width: $grid-unit-80 * 13; + width: calc(100% - #{$grid-unit-80}); + margin-top: $grid-unit-80; + max-height: calc(100% - #{$grid-unit-80 * 2}); + + @include break-large() { + width: calc(100% - #{$grid-unit-80 * 2}); + } +} + +.edit-site-custom-template-modal__contents, +.edit-site-add-new-template__template-list__contents { + > .components-button { + padding: $grid-unit-30; + border-radius: $radius-block-ui; + display: flex; + flex-direction: column; + border: $border-width solid $gray-300; + min-height: $grid-unit-80 * 3; + + // Show the boundary of the button, in High Contrast Mode. + outline: 1px solid transparent; + + span:first-child { + color: $gray-900; + } + + span { + color: $gray-700; + } + + &:hover { + color: var(--wp-admin-theme-color-darker-10); + background: rgba(var(--wp-admin-theme-color--rgb), 0.04); + border-color: transparent; + + span { + color: var(--wp-admin-theme-color); + } + } + + &:focus { + box-shadow: 0 0 0 var(--wp-admin-border-width-focus) var(--wp-admin-theme-color); + border-color: transparent; + + // Windows High Contrast mode will show this outline, but not the box-shadow. + outline: 3px solid transparent; + + span:first-child { + color: var(--wp-admin-theme-color); + } + } + } +} + +.edit-site-add-new-template__template-list__contents { + > .components-button { + height: 100%; + text-align: start; + align-items: flex-start; } } diff --git a/test/e2e/specs/site-editor/site-editor-url-navigation.spec.js b/test/e2e/specs/site-editor/site-editor-url-navigation.spec.js index edc4909a98c97..5668832e4162c 100644 --- a/test/e2e/specs/site-editor/site-editor-url-navigation.spec.js +++ b/test/e2e/specs/site-editor/site-editor-url-navigation.spec.js @@ -45,7 +45,7 @@ test.describe( 'Site editor url navigation', () => { ] ); await page.click( 'role=button[name="Add New Template"i]' ); await page - .getByRole( 'menuitem', { + .getByRole( 'button', { name: 'Single item: Post', } ) .click();