diff --git a/packages/edit-site/src/components/header-edit-mode/document-actions/index.js b/packages/edit-site/src/components/header-edit-mode/document-actions/index.js index e738f4bd23d135..97bb0acb3bdd49 100644 --- a/packages/edit-site/src/components/header-edit-mode/document-actions/index.js +++ b/packages/edit-site/src/components/header-edit-mode/document-actions/index.js @@ -1,112 +1,39 @@ -/** - * External dependencies - */ -import classnames from 'classnames'; - /** * WordPress dependencies */ import { sprintf, __ } from '@wordpress/i18n'; +import { useDispatch } from '@wordpress/data'; import { - __experimentalGetBlockLabel as getBlockLabel, - getBlockType, -} from '@wordpress/blocks'; -import { useSelect } from '@wordpress/data'; -import { - Dropdown, Button, VisuallyHidden, __experimentalText as Text, + __experimentalHStack as HStack, } from '@wordpress/components'; -import { chevronDown } from '@wordpress/icons'; -import { useState, useMemo } from '@wordpress/element'; -import { - store as blockEditorStore, - useBlockDisplayInformation, - BlockIcon, -} from '@wordpress/block-editor'; -import { store as preferencesStore } from '@wordpress/preferences'; +import { BlockIcon } from '@wordpress/block-editor'; +import { privateApis as commandsPrivateApis } from '@wordpress/commands'; /** * Internal dependencies */ -import TemplateDetails from '../../template-details'; import useEditedEntityRecord from '../../use-edited-entity-record'; +import { unlock } from '../../../private-apis'; -function getBlockDisplayText( block ) { - if ( block ) { - const blockType = getBlockType( block.name ); - return blockType ? getBlockLabel( blockType, block.attributes ) : null; - } - return null; -} - -function useSecondaryText() { - const { getBlock } = useSelect( blockEditorStore ); - const activeEntityBlockId = useSelect( - ( select ) => - select( - blockEditorStore - ).__experimentalGetActiveBlockIdByBlockNames( [ - 'core/template-part', - ] ), - [] - ); - - const blockInformation = useBlockDisplayInformation( activeEntityBlockId ); - - if ( activeEntityBlockId ) { - return { - label: getBlockDisplayText( getBlock( activeEntityBlockId ) ), - isActive: true, - icon: blockInformation?.icon, - }; - } - - return {}; -} +const { store: commandsStore } = unlock( commandsPrivateApis ); export default function DocumentActions() { - const showIconLabels = useSelect( - ( select ) => - select( preferencesStore ).get( - 'core/edit-site', - 'showIconLabels' - ), - [] - ); - const { isLoaded, record, getTitle } = useEditedEntityRecord(); - const { label, icon } = useSecondaryText(); - - // Use internal state instead of a ref to make sure that the component - // re-renders when the popover's anchor updates. - const [ popoverAnchor, setPopoverAnchor ] = useState( null ); - - // Memoize popoverProps to avoid returning a new object every time. - const popoverProps = useMemo( - () => ( { - // Use the title wrapper as the popover anchor so that the dropdown is - // centered over the whole title area rather than just one part of it. - anchor: popoverAnchor, - placement: 'bottom', - } ), - [ popoverAnchor ] - ); + const { open: openCommandCenter } = useDispatch( commandsStore ); + const { isLoaded, record, getTitle, icon } = useEditedEntityRecord(); // Return a simple loading indicator until we have information to show. if ( ! isLoaded ) { - return ( -
- { __( 'Loading…' ) } -
- ); + return null; } // Return feedback that the template does not seem to exist. if ( ! record ) { return (
- { __( 'Template not found' ) } + { __( 'Document not found' ) }
); } @@ -116,21 +43,21 @@ export default function DocumentActions() { ? __( 'template part' ) : __( 'template' ); + const isMac = /Mac|iPod|iPhone|iPad/.test( window.navigator.platform ); + return ( -
openCommandCenter() } > -
+ - + + { sprintf( /* translators: %s: the entity being edited, like "template"*/ @@ -140,39 +67,10 @@ export default function DocumentActions() { { getTitle() } -
- - { label ?? '' } -
- - ( - - ) } - contentClassName="edit-site-document-actions__info-dropdown" - renderContent={ ( { onClose } ) => ( - - ) } - /> -
-
+ + + { isMac ? '⌘' : 'Ctrl' } K + + ); } diff --git a/packages/edit-site/src/components/header-edit-mode/document-actions/style.scss b/packages/edit-site/src/components/header-edit-mode/document-actions/style.scss index 43915135ec276b..247b901975fd8e 100644 --- a/packages/edit-site/src/components/header-edit-mode/document-actions/style.scss +++ b/packages/edit-site/src/components/header-edit-mode/document-actions/style.scss @@ -1,79 +1,48 @@ .edit-site-document-actions { display: flex; - flex-direction: column; - justify-content: center; - padding: 0 $grid-unit; - height: 100%; + align-items: center; + gap: $grid-unit; + height: $button-size; + padding: $grid-unit; + justify-content: space-between; // Flex items will, by default, refuse to shrink below a minimum // intrinsic width. In order to shrink this flexbox item, and // subsequently truncate child text, we set an explicit min-width. // See https://dev.w3.org/csswg/css-flexbox/#min-size-auto min-width: 0; + background: $gray-100; + border-radius: 4px; + width: min(100%, 450px); - .edit-site-document-actions__title-wrapper { - display: flex; - flex-direction: row; - justify-content: center; - align-items: center; - - // See comment above about min-width - min-width: 0; - - .components-dropdown { - display: inline-flex; - margin-left: $grid-unit-05; - - .components-button { - min-width: 0; - padding: 0; - } - } - } - - .edit-site-document-actions__title-wrapper > h1 { - margin: 0; - - // See comment above about min-width - min-width: 0; - } - - .edit-site-document-actions__title { - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; + &:hover { color: currentColor; + background: $gray-200; } +} - .edit-site-document-actions__secondary-item { - display: flex; - align-items: center; +.edit-site-document-actions__title { + flex-grow: 1; + color: var(--wp-block-synced-color); + overflow: hidden; + + h1 { + color: var(--wp-block-synced-color); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; - max-width: 0; - opacity: 0; - padding: 0; - transition: all ease 0.2s; - background: rgba(var(--wp-block-synced-color--rgb), 0.04); - border-radius: 2px; - @include reduce-motion(transition); - - .block-editor-block-icon.has-colors { - color: var(--wp-block-synced-color); - } } +} - &.has-secondary-label { - .edit-site-document-actions__secondary-item { - opacity: 1; - padding: 0 4px; - max-width: 180px; - margin-left: 6px; - } +.edit-site-document-actions__shortcut { + flex-shrink: 0; + color: $gray-700; + width: #{$grid-unit * 4.5}; + &:hover { + color: $gray-700; } } -.edit-site-document-actions__info-dropdown > .components-popover__content { - padding: 0; - min-width: 240px; +.edit-site-document-actions__left { + min-width: $button-size; + flex-shrink: 0; } diff --git a/packages/edit-site/src/components/header-edit-mode/style.scss b/packages/edit-site/src/components/header-edit-mode/style.scss index d26bad43e356da..bbaf896076a099 100644 --- a/packages/edit-site/src/components/header-edit-mode/style.scss +++ b/packages/edit-site/src/components/header-edit-mode/style.scss @@ -27,6 +27,7 @@ $header-toolbar-min-width: 335px; align-items: center; height: 100%; flex-grow: 1; + margin: 0 $grid-unit-10; justify-content: center; // Flex items will, by default, refuse to shrink below a minimum // intrinsic width. In order to shrink this flexbox item, and diff --git a/packages/edit-site/src/components/template-details/edit-template-title.js b/packages/edit-site/src/components/template-details/edit-template-title.js deleted file mode 100644 index ac2266a0a03d76..00000000000000 --- a/packages/edit-site/src/components/template-details/edit-template-title.js +++ /dev/null @@ -1,41 +0,0 @@ -/** - * WordPress dependencies - */ -import { __ } from '@wordpress/i18n'; -import { TextControl } from '@wordpress/components'; -import { useEntityProp } from '@wordpress/core-data'; -import { useState } from '@wordpress/element'; - -export default function EditTemplateTitle( { template } ) { - const [ forceEmpty, setForceEmpty ] = useState( false ); - const [ title, setTitle ] = useEntityProp( - 'postType', - template.type, - 'title', - template.id - ); - - return ( - { - if ( ! newTitle && ! forceEmpty ) { - setForceEmpty( true ); - return; - } - setForceEmpty( false ); - setTitle( newTitle ); - } } - onBlur={ () => setForceEmpty( false ) } - /> - ); -} diff --git a/packages/edit-site/src/components/template-details/index.js b/packages/edit-site/src/components/template-details/index.js deleted file mode 100644 index 4f0c23755f857c..00000000000000 --- a/packages/edit-site/src/components/template-details/index.js +++ /dev/null @@ -1,113 +0,0 @@ -/** - * WordPress dependencies - */ -import { __ } from '@wordpress/i18n'; -import { - Button, - MenuGroup, - MenuItem, - __experimentalVStack as VStack, - __experimentalText as Text, -} from '@wordpress/components'; -import { useDispatch, useSelect } from '@wordpress/data'; -import { store as editorStore } from '@wordpress/editor'; -import { decodeEntities } from '@wordpress/html-entities'; - -/** - * Internal dependencies - */ -import isTemplateRevertable from '../../utils/is-template-revertable'; -import { store as editSiteStore } from '../../store'; -import EditTemplateTitle from './edit-template-title'; -import { useLink } from '../routes/link'; -import TemplatePartAreaSelector from './template-part-area-selector'; - -export default function TemplateDetails( { template, onClose } ) { - const { title, description } = useSelect( - ( select ) => - select( editorStore ).__experimentalGetTemplateInfo( template ), - [] - ); - const { revertTemplate } = useDispatch( editSiteStore ); - - // TODO: We should update this to filter by template part's areas as well. - const browseAllLinkProps = useLink( { - path: '/' + template.type + '/all', - } ); - - const isTemplatePart = template.type === 'wp_template_part'; - - // Only user-created and non-default templates can change the name. - // But any user-created template part can be renamed. - const canEditTitle = isTemplatePart - ? ! template.has_theme_file - : template.is_custom && ! template.has_theme_file; - - if ( ! template ) { - return null; - } - - const revert = () => { - revertTemplate( template ); - onClose(); - }; - - return ( -
- - { canEditTitle ? ( - - ) : ( - - { decodeEntities( title ) } - - ) } - - { description && ( - - { decodeEntities( description ) } - - ) } - - - { isTemplatePart && ( -
- -
- ) } - - { isTemplateRevertable( template ) && ( - - - { __( 'Clear customizations' ) } - - - ) } - - -
- ); -} diff --git a/packages/edit-site/src/components/template-details/style.scss b/packages/edit-site/src/components/template-details/style.scss deleted file mode 100644 index ee840be7c43db7..00000000000000 --- a/packages/edit-site/src/components/template-details/style.scss +++ /dev/null @@ -1,72 +0,0 @@ -.edit-site-template-details { - .edit-site-template-details__group { - margin: 0; - padding: $grid-unit-20; - } - - .edit-site-template-details__group + .edit-site-template-details__group { - border-top: $border-width solid $gray-400; - } - - .edit-site-template-details__description { - color: $gray-700; - } - - // The group already has a 8px padding inside, so overriding the parent padding. - .edit-site-template-details__group.edit-site-template-details__template-areas { - padding: $grid-unit-10; - } - - .edit-site-template-details__template-areas-item { - position: relative; - - .components-menu-items__item-icon { - color: var(--wp-block-synced-color); - } - - .edit-site-template-details__template-areas-item-more { - position: absolute; - right: 0; - top: 0; - bottom: 0; - margin: auto 0; - } - } - - .edit-site-template-details__revert { - // Make some spaces for the revert button to have some paddings. - padding: $grid-unit-15 $grid-unit; - } - - .edit-site-template-details__revert-button { - height: auto; - padding: $grid-unit-05 $grid-unit; - text-align: left; - - &:focus:not(:disabled) { - box-shadow: inset 0 0 0 var(--wp-admin-border-width-focus) var(--wp-admin-theme-color), inset 0 0 0 3px $white; - } - } - - .edit-site-template-details__show-all-button.components-button { - display: flex; - justify-content: center; - background: $gray-900; - color: $white; - width: 100%; - height: ($button-size + $grid-unit-10); - border-radius: 0; - - &:hover { - color: $white; - } - - &:active { - color: $gray-400; - } - - &:focus:not(:disabled) { - box-shadow: inset 0 0 0 var(--wp-admin-border-width-focus) var(--wp-admin-theme-color), inset 0 0 0 3px $white; - } - } -} diff --git a/packages/edit-site/src/components/template-details/template-areas.js b/packages/edit-site/src/components/template-details/template-areas.js deleted file mode 100644 index 2c6a509ddc9d64..00000000000000 --- a/packages/edit-site/src/components/template-details/template-areas.js +++ /dev/null @@ -1,167 +0,0 @@ -/** - * WordPress dependencies - */ -import { sprintf, __ } from '@wordpress/i18n'; -import { DropdownMenu, MenuGroup, MenuItem } from '@wordpress/components'; -import { useSelect, useDispatch } from '@wordpress/data'; -import { store as editorStore } from '@wordpress/editor'; -import { store as blockEditorStore } from '@wordpress/block-editor'; -import { moreVertical } from '@wordpress/icons'; -import { privateApis as routerPrivateApis } from '@wordpress/router'; - -/** - * Internal dependencies - */ -import { store as editSiteStore } from '../../store'; -import isTemplateRevertable from '../../utils/is-template-revertable'; -import { useLink } from '../routes/link'; -import { unlock } from '../../private-apis'; - -const { useLocation } = unlock( routerPrivateApis ); - -function TemplatePartItemMore( { - onClose, - templatePart, - closeTemplateDetailsDropdown, -} ) { - const { revertTemplate } = useDispatch( editSiteStore ); - const { params } = useLocation(); - const editLinkProps = useLink( - { - postId: templatePart.id, - postType: templatePart.type, - }, - { - fromTemplateId: params.postId, - } - ); - - function editTemplatePart( event ) { - editLinkProps.onClick( event ); - onClose(); - closeTemplateDetailsDropdown(); - } - - function clearCustomizations() { - revertTemplate( templatePart ); - onClose(); - closeTemplateDetailsDropdown(); - } - - return ( - <> - - - { sprintf( - /* translators: %s: template part title */ - __( 'Edit %s' ), - templatePart.title?.rendered - ) } - - - { isTemplateRevertable( templatePart ) && ( - - - { __( 'Clear customizations' ) } - - - ) } - - ); -} - -function TemplatePartItem( { - templatePart, - clientId, - closeTemplateDetailsDropdown, -} ) { - const { selectBlock, toggleBlockHighlight } = - useDispatch( blockEditorStore ); - const templatePartArea = useSelect( - ( select ) => { - const defaultAreas = - select( - editorStore - ).__experimentalGetDefaultTemplatePartAreas(); - - return defaultAreas.find( - ( defaultArea ) => defaultArea.area === templatePart.area - ); - }, - [ templatePart.area ] - ); - const highlightBlock = () => toggleBlockHighlight( clientId, true ); - const cancelHighlightBlock = () => toggleBlockHighlight( clientId, false ); - - return ( -
- { - selectBlock( clientId ); - } } - onMouseOver={ highlightBlock } - onMouseLeave={ cancelHighlightBlock } - onFocus={ highlightBlock } - onBlur={ cancelHighlightBlock } - > - { templatePartArea?.label } - - - - { ( { onClose } ) => ( - - ) } - -
- ); -} - -export default function TemplateAreas( { closeTemplateDetailsDropdown } ) { - const templateParts = useSelect( - ( select ) => select( editSiteStore ).getCurrentTemplateTemplateParts(), - [] - ); - - if ( ! templateParts.length ) { - return null; - } - - return ( - - { templateParts.map( ( { templatePart, block } ) => ( - - ) ) } - - ); -} diff --git a/packages/edit-site/src/components/template-details/template-part-area-selector.js b/packages/edit-site/src/components/template-details/template-part-area-selector.js deleted file mode 100644 index 4823fea501ce14..00000000000000 --- a/packages/edit-site/src/components/template-details/template-part-area-selector.js +++ /dev/null @@ -1,39 +0,0 @@ -/** - * WordPress dependencies - */ -import { __ } from '@wordpress/i18n'; -import { SelectControl } from '@wordpress/components'; -import { useEntityProp } from '@wordpress/core-data'; -import { useSelect } from '@wordpress/data'; -import { store as editorStore } from '@wordpress/editor'; - -export default function TemplatePartAreaSelector( { id } ) { - const [ area, setArea ] = useEntityProp( - 'postType', - 'wp_template_part', - 'area', - id - ); - - const definedAreas = useSelect( - ( select ) => - select( editorStore ).__experimentalGetDefaultTemplatePartAreas(), - [] - ); - - const areaOptions = definedAreas.map( ( { label, area: _area } ) => ( { - label, - value: _area, - } ) ); - - return ( - - ); -} diff --git a/packages/edit-site/src/components/use-edited-entity-record/index.js b/packages/edit-site/src/components/use-edited-entity-record/index.js index 99da66d9d23c08..ef887eeb56f99e 100644 --- a/packages/edit-site/src/components/use-edited-entity-record/index.js +++ b/packages/edit-site/src/components/use-edited-entity-record/index.js @@ -12,7 +12,7 @@ import { decodeEntities } from '@wordpress/html-entities'; import { store as editSiteStore } from '../../store'; export default function useEditedEntityRecord( postType, postId ) { - const { record, title, description, isLoaded } = useSelect( + const { record, title, description, isLoaded, icon } = useSelect( ( select ) => { const { getEditedPostType, getEditedPostId } = select( editSiteStore ); @@ -34,6 +34,7 @@ export default function useEditedEntityRecord( postType, postId ) { title: templateInfo.title, description: templateInfo.description, isLoaded: _isLoaded, + icon: templateInfo.icon, }; }, [ postType, postId ] @@ -41,6 +42,7 @@ export default function useEditedEntityRecord( postType, postId ) { return { isLoaded, + icon, record, getTitle: () => ( title ? decodeEntities( title ) : null ), getDescription: () => diff --git a/packages/edit-site/src/style.scss b/packages/edit-site/src/style.scss index 679d13a08277ac..7a0233ebee5247 100644 --- a/packages/edit-site/src/style.scss +++ b/packages/edit-site/src/style.scss @@ -13,7 +13,6 @@ @import "./components/sidebar-edit-mode/settings-header/style.scss"; @import "./components/sidebar-edit-mode/template-card/style.scss"; @import "./components/editor/style.scss"; -@import "./components/template-details/style.scss"; @import "./components/create-template-part-modal/style.scss"; @import "./components/secondary-sidebar/style.scss"; @import "./components/welcome-guide/style.scss"; diff --git a/test/e2e/specs/site-editor/browser-history.spec.js b/test/e2e/specs/site-editor/browser-history.spec.js index bbfd2e8c5b86b8..2dec72953765f8 100644 --- a/test/e2e/specs/site-editor/browser-history.spec.js +++ b/test/e2e/specs/site-editor/browser-history.spec.js @@ -35,26 +35,4 @@ test.describe( 'Site editor browser history', () => { await page.goBack(); await expect( page ).toHaveURL( '/wp-admin/index.php' ); } ); - - test( 'Opens the template list from the template details view', async ( { - admin, - page, - } ) => { - await admin.visitSiteEditor( { - postType: 'wp_template', - postId: 'emptytheme//index', - canvas: 'edit', - } ); - - // Navigate to the template list - await page.click( 'role=button[name="Show template details"]' ); - await page.click( 'role=link[name="Manage all templates"]' ); - - await expect( page ).toHaveURL( - '/wp-admin/site-editor.php?path=%2Fwp_template%2Fall' - ); - - const title = page.getByRole( 'heading', { level: 1 } ); - await expect( title ).toHaveText( 'Templates' ); - } ); } ); diff --git a/test/e2e/specs/site-editor/style-book.spec.js b/test/e2e/specs/site-editor/style-book.spec.js index 5cdf1c2a0e59e7..6231175584554b 100644 --- a/test/e2e/specs/site-editor/style-book.spec.js +++ b/test/e2e/specs/site-editor/style-book.spec.js @@ -40,9 +40,6 @@ test.describe( 'Style Book', () => { await expect( page.locator( 'role=button[name="Redo"i]' ) ).not.toBeVisible(); - await expect( - page.locator( 'role=button[name="Show template details"i]' ) - ).not.toBeVisible(); await expect( page.locator( 'role=button[name="View"i]' ) ).not.toBeVisible(); diff --git a/test/e2e/specs/site-editor/template-revert.spec.js b/test/e2e/specs/site-editor/template-revert.spec.js index f1f6b3eb5d014e..a13abab881d9b9 100644 --- a/test/e2e/specs/site-editor/template-revert.spec.js +++ b/test/e2e/specs/site-editor/template-revert.spec.js @@ -39,11 +39,23 @@ test.describe( 'Template Revert', () => { await templateRevertUtils.revertTemplate(); await editor.saveSiteEditorEntities(); - await page.click( 'role=button[name="Show template details"i]' ); + await page.click( 'role=button[name="Settings"i]' ); + const isTemplateTabVisible = await page + .locator( + 'role=region[name="Editor settings"i] >> role=button[name="Template"i]' + ) + .isVisible(); + if ( isTemplateTabVisible ) { + await page.click( + 'role=region[name="Editor settings"i] >> role=button[name="Template"i]' + ); + } // The revert button isn't visible anymore. await expect( - page.locator( 'role=menuitem[name=/Clear customizations/i]' ) + page.locator( + 'role=region[name="Editor settings"i] >> role=button[name="Actions"i]' + ) ).not.toBeVisible(); } ); @@ -279,11 +291,25 @@ class TemplateRevertUtils { } async revertTemplate() { - await this.page.click( 'role=button[name="Show template details"i]' ); + await this.page.click( 'role=button[name="Settings"i]' ); + const isTemplateTabVisible = await this.page + .locator( + 'role=region[name="Editor settings"i] >> role=button[name="Template"i]' + ) + .isVisible(); + if ( isTemplateTabVisible ) { + await this.page.click( + 'role=region[name="Editor settings"i] >> role=button[name="Template"i]' + ); + } + await this.page.click( + 'role=region[name="Editor settings"i] >> role=button[name="Actions"i]' + ); await this.page.click( 'role=menuitem[name=/Clear customizations/i]' ); await this.page.waitForSelector( 'role=button[name="Dismiss this notice"i] >> text="Template reverted."' ); + await this.page.click( 'role=button[name="Settings"i]' ); } async getCurrentSiteEditorContent() { diff --git a/test/e2e/specs/site-editor/title.spec.js b/test/e2e/specs/site-editor/title.spec.js index 585b75c1507be3..21cfc544829705 100644 --- a/test/e2e/specs/site-editor/title.spec.js +++ b/test/e2e/specs/site-editor/title.spec.js @@ -45,29 +45,4 @@ test.describe( 'Site editor title', () => { await expect( title ).toHaveText( 'Editing template part: header' ); } ); - - test( "displays the selected template part's name in the secondary title when a template part is selected from List View", async ( { - admin, - page, - } ) => { - await admin.visitSiteEditor( { - postId: 'emptytheme//index', - postType: 'wp_template', - canvas: 'edit', - } ); - // Select the header template part via list view. - await page.click( 'role=button[name="List View"i]' ); - const listView = page.locator( - 'role=treegrid[name="Block navigation structure"i]' - ); - await listView.locator( 'role=gridcell >> text="header"' ).click(); - await page.click( 'role=button[name="Close"i]' ); - - // Evaluate the document settings secondary title. - const secondaryTitle = page.locator( - '.edit-site-document-actions__secondary-item' - ); - - await expect( secondaryTitle ).toHaveText( 'header' ); - } ); } );