From cd22276e890fd611076a64083d964d06e0cfc33f Mon Sep 17 00:00:00 2001 From: Aaron Robertshaw <60436221+aaronrobertshaw@users.noreply.github.com> Date: Fri, 23 Jun 2023 14:22:10 +1000 Subject: [PATCH] Library: Reinstate sidebar navigation menu editing for template parts (#51825) --- .../index.js | 94 +++---------------- ...template-part-navigation-menu-list-item.js | 28 ++++++ .../template-part-navigation-menu-list.js | 21 +++++ .../template-part-navigation-menu.js | 30 ++++++ .../template-part-navigation-menus.js | 33 +++++++ .../use-navigation-menu-content.js | 71 ++++++++++++++ .../use-pattern-details.js | 86 +++++++++++++++++ 7 files changed, 280 insertions(+), 83 deletions(-) create mode 100644 packages/edit-site/src/components/sidebar-navigation-screen-pattern/template-part-navigation-menu-list-item.js create mode 100644 packages/edit-site/src/components/sidebar-navigation-screen-pattern/template-part-navigation-menu-list.js create mode 100644 packages/edit-site/src/components/sidebar-navigation-screen-pattern/template-part-navigation-menu.js create mode 100644 packages/edit-site/src/components/sidebar-navigation-screen-pattern/template-part-navigation-menus.js create mode 100644 packages/edit-site/src/components/sidebar-navigation-screen-pattern/use-navigation-menu-content.js create mode 100644 packages/edit-site/src/components/sidebar-navigation-screen-pattern/use-pattern-details.js diff --git a/packages/edit-site/src/components/sidebar-navigation-screen-pattern/index.js b/packages/edit-site/src/components/sidebar-navigation-screen-pattern/index.js index 149f420ff6fb9b..279b5a0de298d8 100644 --- a/packages/edit-site/src/components/sidebar-navigation-screen-pattern/index.js +++ b/packages/edit-site/src/components/sidebar-navigation-screen-pattern/index.js @@ -1,91 +1,21 @@ /** * WordPress dependencies */ -import { __, sprintf, _x } from '@wordpress/i18n'; -import { useDispatch, useSelect } from '@wordpress/data'; +import { __ } from '@wordpress/i18n'; +import { useDispatch } from '@wordpress/data'; import { pencil } from '@wordpress/icons'; -import { - __experimentalUseNavigator as useNavigator, - Icon, -} from '@wordpress/components'; -import { store as coreStore } from '@wordpress/core-data'; +import { __experimentalUseNavigator as useNavigator } from '@wordpress/components'; /** * Internal dependencies */ +import SidebarButton from '../sidebar-button'; import SidebarNavigationScreen from '../sidebar-navigation-screen'; -import useEditedEntityRecord from '../use-edited-entity-record'; import useInitEditedEntityFromURL from '../sync-state-with-url/use-init-edited-entity-from-url'; -import { unlock } from '../../lock-unlock'; +import usePatternDetails from './use-pattern-details'; +import useNavigationMenuContent from './use-navigation-menu-content'; import { store as editSiteStore } from '../../store'; -import SidebarButton from '../sidebar-button'; -import { useAddedBy } from '../list/added-by'; - -function usePatternTitleAndDescription( postType, postId ) { - const { getDescription, getTitle, record } = useEditedEntityRecord( - postType, - postId - ); - const currentTheme = useSelect( - ( select ) => select( coreStore ).getCurrentTheme(), - [] - ); - const addedBy = useAddedBy( postType, postId ); - const isAddedByActiveTheme = - addedBy.type === 'theme' && record.theme === currentTheme?.stylesheet; - const title = getTitle(); - let descriptionText = getDescription(); - - if ( ! descriptionText && addedBy.text ) { - descriptionText = sprintf( - // translators: %s: pattern title e.g: "Header". - __( 'This is your %s pattern.' ), - getTitle() - ); - } - - if ( ! descriptionText && postType === 'wp_block' && record?.title ) { - descriptionText = sprintf( - // translators: %s: user created pattern title e.g. "Footer". - __( 'This is your %s pattern.' ), - record.title - ); - } - - const description = ( - <> - { descriptionText } - - { addedBy.text && ! isAddedByActiveTheme && ( - - - - { addedBy.imageUrl ? ( - - ) : ( - - ) } - - { addedBy.text } - - - { addedBy.isCustomized && ( - - { _x( '(Customized)', 'pattern' ) } - - ) } - - ) } - - ); - - return { title, description }; -} +import { unlock } from '../../lock-unlock'; export default function SidebarNavigationScreenPattern() { const { params } = useNavigator(); @@ -94,14 +24,11 @@ export default function SidebarNavigationScreenPattern() { useInitEditedEntityFromURL(); - const { title, description } = usePatternTitleAndDescription( - postType, - postId - ); + const patternDetails = usePatternDetails( postType, postId ); + const content = useNavigationMenuContent( postType, postId ); return ( setCanvasMode( 'edit' ) } @@ -110,7 +37,8 @@ export default function SidebarNavigationScreenPattern() { /> } backPath={ '/library' } - description={ description } + content={ content } + { ...patternDetails } /> ); } diff --git a/packages/edit-site/src/components/sidebar-navigation-screen-pattern/template-part-navigation-menu-list-item.js b/packages/edit-site/src/components/sidebar-navigation-screen-pattern/template-part-navigation-menu-list-item.js new file mode 100644 index 00000000000000..b685c766107a32 --- /dev/null +++ b/packages/edit-site/src/components/sidebar-navigation-screen-pattern/template-part-navigation-menu-list-item.js @@ -0,0 +1,28 @@ +/** + * WordPress dependencies + */ +import { useEntityProp } from '@wordpress/core-data'; +import { __ } from '@wordpress/i18n'; + +/** + * Internal dependencies + */ +import SidebarNavigationItem from '../sidebar-navigation-item'; +import { useLink } from '../routes/link'; + +export default function TemplatePartNavigationMenuListItem( { id } ) { + const [ title ] = useEntityProp( 'postType', 'wp_navigation', 'title', id ); + + const linkInfo = useLink( { + postId: id, + postType: 'wp_navigation', + } ); + + if ( ! id ) return null; + + return ( + + { title || __( '(no title)' ) } + + ); +} diff --git a/packages/edit-site/src/components/sidebar-navigation-screen-pattern/template-part-navigation-menu-list.js b/packages/edit-site/src/components/sidebar-navigation-screen-pattern/template-part-navigation-menu-list.js new file mode 100644 index 00000000000000..4171b1e782575e --- /dev/null +++ b/packages/edit-site/src/components/sidebar-navigation-screen-pattern/template-part-navigation-menu-list.js @@ -0,0 +1,21 @@ +/** + * WordPress dependencies + */ +import { __experimentalItemGroup as ItemGroup } from '@wordpress/components'; +/** + * Internal dependencies + */ +import TemplatePartNavigationMenuListItem from './template-part-navigation-menu-list-item'; + +export default function TemplatePartNavigationMenuList( { menus } ) { + return ( + + { menus.map( ( menuId ) => ( + + ) ) } + + ); +} diff --git a/packages/edit-site/src/components/sidebar-navigation-screen-pattern/template-part-navigation-menu.js b/packages/edit-site/src/components/sidebar-navigation-screen-pattern/template-part-navigation-menu.js new file mode 100644 index 00000000000000..f451c17e00adb7 --- /dev/null +++ b/packages/edit-site/src/components/sidebar-navigation-screen-pattern/template-part-navigation-menu.js @@ -0,0 +1,30 @@ +/** + * WordPress dependencies + */ +import { __ } from '@wordpress/i18n'; +import { __experimentalHeading as Heading } from '@wordpress/components'; +import { useEntityProp } from '@wordpress/core-data'; + +/** + * Internal dependencies + */ +import NavigationMenuEditor from '../sidebar-navigation-screen-navigation-menu/navigation-menu-editor'; + +export default function TemplatePartNavigationMenu( { id } ) { + const [ title ] = useEntityProp( 'postType', 'wp_navigation', 'title', id ); + + if ( ! id ) return null; + + return ( + <> + + { title?.rendered || __( 'Navigation' ) } + + + + ); +} diff --git a/packages/edit-site/src/components/sidebar-navigation-screen-pattern/template-part-navigation-menus.js b/packages/edit-site/src/components/sidebar-navigation-screen-pattern/template-part-navigation-menus.js new file mode 100644 index 00000000000000..418b1d4423b202 --- /dev/null +++ b/packages/edit-site/src/components/sidebar-navigation-screen-pattern/template-part-navigation-menus.js @@ -0,0 +1,33 @@ +/** + * WordPress dependencies + */ +import { __ } from '@wordpress/i18n'; +import { __experimentalHeading as Heading } from '@wordpress/components'; +/** + * Internal dependencies + */ +import TemplatePartNavigationMenu from './template-part-navigation-menu'; +import TemplatePartNavigationMenuList from './template-part-navigation-menu-list'; + +export default function TemplatePartNavigationMenus( { menus } ) { + if ( ! menus.length ) return null; + + // if there is a single menu then render TemplatePartNavigationMenu + if ( menus.length === 1 ) { + return ; + } + + // if there are multiple menus then render TemplatePartNavigationMenuList + return ( + <> + + { __( 'Navigation' ) } + + + + ); +} diff --git a/packages/edit-site/src/components/sidebar-navigation-screen-pattern/use-navigation-menu-content.js b/packages/edit-site/src/components/sidebar-navigation-screen-pattern/use-navigation-menu-content.js new file mode 100644 index 00000000000000..84f09c19ff3b0d --- /dev/null +++ b/packages/edit-site/src/components/sidebar-navigation-screen-pattern/use-navigation-menu-content.js @@ -0,0 +1,71 @@ +/** + * Internal dependencies + */ +import TemplatePartNavigationMenus from './template-part-navigation-menus'; +import useEditedEntityRecord from '../use-edited-entity-record'; + +/** + * Retrieves a list of specific blocks from a given tree of blocks. + * + * @param {string} targetBlockType The name of the block type to find. + * @param {Array} blocks A list of blocks from a template part entity. + * + * @return {Array} A list of any navigation blocks found in the blocks. + */ +function getBlocksOfTypeFromBlocks( targetBlockType, blocks ) { + if ( ! targetBlockType || ! blocks?.length ) { + return []; + } + + const findInBlocks = ( _blocks ) => { + if ( ! _blocks ) { + return []; + } + + const navigationBlocks = []; + + for ( const block of _blocks ) { + if ( block.name === targetBlockType ) { + navigationBlocks.push( block ); + } + + if ( block?.innerBlocks ) { + const innerNavigationBlocks = findInBlocks( block.innerBlocks ); + + if ( innerNavigationBlocks.length ) { + navigationBlocks.push( ...innerNavigationBlocks ); + } + } + } + + return navigationBlocks; + }; + + return findInBlocks( blocks ); +} + +export default function useNavigationMenuContent( postType, postId ) { + const { record } = useEditedEntityRecord( postType, postId ); + + // Only managing navigation menus in template parts is supported + // to match previous behaviour. This could potentially be expanded + // to patterns as well. + if ( postType !== 'wp_template_part' ) { + return; + } + + const navigationBlocks = getBlocksOfTypeFromBlocks( + 'core/navigation', + record?.blocks + ); + + const navigationMenuIds = navigationBlocks?.map( + ( block ) => block.attributes.ref + ); + + if ( ! navigationMenuIds?.length ) { + return; + } + + return ; +} diff --git a/packages/edit-site/src/components/sidebar-navigation-screen-pattern/use-pattern-details.js b/packages/edit-site/src/components/sidebar-navigation-screen-pattern/use-pattern-details.js new file mode 100644 index 00000000000000..028b1c781f8f2c --- /dev/null +++ b/packages/edit-site/src/components/sidebar-navigation-screen-pattern/use-pattern-details.js @@ -0,0 +1,86 @@ +/** + * WordPress dependencies + */ +import { __, sprintf, _x } from '@wordpress/i18n'; +import { store as coreStore } from '@wordpress/core-data'; +import { useSelect } from '@wordpress/data'; +import { Icon } from '@wordpress/components'; + +/** + * Internal dependencies + */ +import { useAddedBy } from '../list/added-by'; +import useEditedEntityRecord from '../use-edited-entity-record'; +import SidebarNavigationScreenDetailsFooter from '../sidebar-navigation-screen-details-footer'; + +export default function usePatternDetails( postType, postId ) { + const { getDescription, getTitle, record } = useEditedEntityRecord( + postType, + postId + ); + const currentTheme = useSelect( + ( select ) => select( coreStore ).getCurrentTheme(), + [] + ); + const addedBy = useAddedBy( postType, postId ); + const isAddedByActiveTheme = + addedBy.type === 'theme' && record.theme === currentTheme?.stylesheet; + const title = getTitle(); + let descriptionText = getDescription(); + + if ( ! descriptionText && addedBy.text ) { + descriptionText = sprintf( + // translators: %s: pattern title e.g: "Header". + __( 'This is your %s pattern.' ), + getTitle() + ); + } + + if ( ! descriptionText && postType === 'wp_block' && record?.title ) { + descriptionText = sprintf( + // translators: %s: user created pattern title e.g. "Footer". + __( 'This is your %s pattern.' ), + record.title + ); + } + + const description = ( + <> + { descriptionText } + + { addedBy.text && ! isAddedByActiveTheme && ( + + + + { addedBy.imageUrl ? ( + + ) : ( + + ) } + + { addedBy.text } + + + { addedBy.isCustomized && ( + + { _x( '(Customized)', 'pattern' ) } + + ) } + + ) } + + ); + + const footer = !! record?.modified ? ( + + ) : null; + + return { title, description, footer }; +}