Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

FSE: Add transform + upgrade nudge from A8c Nav Block to Core Nav Block #38197

Closed
wants to merge 13 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* WordPress dependencies
*/
import ServerSideRender from '@wordpress/server-side-render';
import { Fragment } from '@wordpress/element';
import { Fragment, useState } from '@wordpress/element';
import { compose } from '@wordpress/compose';
import { __ } from '@wordpress/i18n';
import {
Expand All @@ -17,12 +17,14 @@ import {
withColors,
withFontSizes,
} from '@wordpress/block-editor';
import { PanelBody } from '@wordpress/components';
import { withSelect } from '@wordpress/data';
import { PanelBody, Notice } from '@wordpress/components';
import { withSelect, withDispatch } from '@wordpress/data';
import { getPossibleBlockTransformations, switchToBlockType } from '@wordpress/blocks';

/**
* Internal dependencies
*/
import { isValidCoreNavigationBlockType } from './transforms';

const NavigationMenuEdit = ( {
attributes,
Expand All @@ -34,13 +36,35 @@ const NavigationMenuEdit = ( {
setTextColor,
textColor,
isPublished,
canBeUpgradedToCoreNavigationBlock,
upgradeToCoreNavigationBlock,
} ) => {
const [ upgradeNudgeVisible, setUpgradeNudgeVisible ] = useState( true );

const { customFontSize, textAlign } = attributes;

const actualFontSize = customFontSize || fontSize.size;

return (
<Fragment>
{ upgradeNudgeVisible && canBeUpgradedToCoreNavigationBlock && (
<Notice
onRemove={ () => setUpgradeNudgeVisible( false ) }
actions={ [
{
label: __( 'Update Block', 'full-site-editing' ),
onClick: upgradeToCoreNavigationBlock,
},
] }
>
<p>
{ __(
'An improved version of this block is now available. Update and then preview before updating your site.',
'full-site-editing'
) }
</p>
</Notice>
) }
<BlockControls>
<AlignmentToolbar
value={ textAlign }
Expand Down Expand Up @@ -90,9 +114,34 @@ const NavigationMenuEdit = ( {
export default compose( [
withColors( 'backgroundColor', { textColor: 'color' } ),
withFontSizes( 'fontSize' ),
withSelect( select => {
withSelect( ( select, { clientId } ) => {
const block = select( 'core/block-editor' ).getBlock( clientId );

const possibleTransforms = getPossibleBlockTransformations( [ block ] );

const coreNavigationBlockUpgradeTransform = possibleTransforms.find(
transform => transform && isValidCoreNavigationBlockType( transform.name )
);

const canBeUpgradedToCoreNavigationBlock = !! coreNavigationBlockUpgradeTransform;

return {
isPublished: select( 'core/editor' ).isCurrentPostPublished(),
coreNavigationBlockUpgradeTransform,
canBeUpgradedToCoreNavigationBlock,
block,
};
} ),
withDispatch( ( dispatch, { clientId, block, coreNavigationBlockUpgradeTransform } ) => {
const upgradeToCoreNavigationBlock = () => {
return dispatch( 'core/block-editor' ).replaceBlocks(
clientId,
switchToBlockType( block, coreNavigationBlockUpgradeTransform.name )
);
};

return {
upgradeToCoreNavigationBlock,
};
} ),
] )( NavigationMenuEdit );
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { __ } from '@wordpress/i18n';
* Internal dependencies
*/
import edit from './edit';
import transforms from './transforms';
import './style.scss';

const icon = (
Expand All @@ -30,4 +31,5 @@ registerBlockType( 'a8c/navigation-menu', {
},
edit,
save: () => null,
transforms,
} );
Original file line number Diff line number Diff line change
Expand Up @@ -61,18 +61,21 @@ function render_navigation_menu_block( $attributes ) {

$container_class .= $class;
$toggle_class .= $class;
$menu_location = 'menu-1';

$menu = wp_nav_menu(
[
array(
'echo' => false,
'fallback_cb' => 'get_fallback_navigation_menu',
'items_wrap' => '<ul id="%1$s" class="%2$s" aria-label="submenu">%3$s</ul>',
'menu_class' => 'main-menu footer-menu',
'theme_location' => 'menu-1',
'theme_location' => $menu_location,
'container' => '',
]
)
);

$menu_items = get_nav_menu_items_data( $menu_location );

ob_start();
// phpcs:disable WordPress.Security.EscapeOutput.OutputNotEscaped
?>
Expand All @@ -90,11 +93,62 @@ function render_navigation_menu_block( $attributes ) {
</div>
</nav>
<!-- #site-navigation -->
<?php
<?php if ( ! empty( $menu_items ) ) : ?>
<script id="<?php echo esc_attr( uniqid() ); ?>" type="application/json">
<?php
echo \wp_json_encode( $menu_items );
?>
</script>
<?php
endif;

// phpcs:enable WordPress.Security.EscapeOutput.OutputNotEscaped
return ob_get_clean();
}

/**
* Gets data about the Nav items for a given menu location.
* Aims to mirror usage of calling `wp_nav_menu()` without the
* `menu` argument. This is to ensure we're getting the exact same
* Menu items to pass to the editor for use in the transform
* to the core/navigation Block.
*
* See:
* https://core.trac.wordpress.org/browser/tags/5.3/src/wp-includes/nav-menu-template.php#L120.
*
* @param string $menu_location the name of the theme location for the menu.
* @return bool|array false or an array of nav menu objects.
*/
function get_nav_menu_items_data( string $menu_location ) {
if ( ! $menu_location ) {
return false;
}

// Access all Nav Menu Theme locations.
$locations = \get_nav_menu_locations();

if ( empty( $locations ) || empty( $locations[ $menu_location ] ) ) {
return \get_pages();
}

// Get the full Nav Menu object from the location
// https://codex.wordpress.org/Function_Reference/wp_get_nav_menu_object.
$menu_obj = \wp_get_nav_menu_object( $locations[ $menu_location ] );

if ( empty( $menu_obj ) || empty( $menu_obj->term_id ) ) {
return false;
}

// Retrieve data on the individual Menu items.
$nav_items = \wp_get_nav_menu_items( $menu_obj->term_id );

if ( empty( $nav_items ) ) {
$nav_items = \get_pages();
}

return $nav_items;
}

/**
* Render a list of all site pages as a fallback
* for when a menu does not exist.
Expand All @@ -103,23 +157,23 @@ function render_navigation_menu_block( $attributes ) {
*/
function get_fallback_navigation_menu() {
$menu = wp_page_menu(
[
array(
'after' => false,
'before' => false,
'container' => 'ul',
'echo' => false,
'menu_class' => 'main-menu footer-menu',
'sort_column' => 'menu_order, post_date',
]
)
);

/**
* Filter the fallback page menu to use the same
* CSS class structure as a regularly built menu
* so we don't have to duplicate CSS selectors everywhere.
*/
$original_classes = [ 'children', 'page_item_has_sub-menu' ];
$replacement_classes = [ 'sub-menu', 'menu-item-has-children' ];
$original_classes = array( 'children', 'page_item_has_sub-menu' );
$replacement_classes = array( 'sub-menu', 'menu-item-has-children' );

return str_replace( $original_classes, $replacement_classes, $menu );
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,34 @@
$gutenberg-default-font-size: 13px;
$gutenberg-default-line-height: 1.4;

[data-type='a8c/navigation-menu'] {

.components-notice {

&.is-dismissible {
padding-right: 0;
}

.components-notice__content p {
margin-top: 0;
margin-bottom: 1em;
font-size: $gutenberg-default-font-size;
line-height: $gutenberg-default-line-height;
}

.components-notice__action {
margin-bottom: 0.2em;
margin-left: 0;
}

.components-notice__dismiss {
position: relative;
top: -8px;
}

}
}

.wp-block-a8c-navigation-menu.main-navigation {
pointer-events: none;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/**
* External dependencies
*/

/* eslint-disable import/no-extraneous-dependencies */
/**
* WordPress dependencies
*/
import { createBlock } from '@wordpress/blocks';
/* eslint-enable import/no-extraneous-dependencies */

const CORE_NAVIGATION_BLOCK_TYPE = 'core/navigation';
const CORE_NAVIGATION_ITEM_BLOCK_TYPE = 'core/navigation-link';
export const isValidCoreNavigationBlockType = type => CORE_NAVIGATION_BLOCK_TYPE === type;

export default {
to: [
{
type: 'block',
blocks: [ CORE_NAVIGATION_BLOCK_TYPE ],
__experimentalConvert( { attributes, clientId } ) {
let menuItems;
let navItems;

const menuItemScriptTag = document.querySelector( `#block-${ clientId } script` );

if ( menuItemScriptTag ) {
// Existing Menu items are stored as JSON alongside the ServerSideRendered
// block HTML markup.
menuItems = JSON.parse( menuItemScriptTag.innerHTML );
}

if ( menuItems ) {
// Each Menu Items should become a Nav Link Block
navItems = menuItems.map( item => {
return createBlock( CORE_NAVIGATION_ITEM_BLOCK_TYPE, {
label: item.post_title || item.title,
title: item.post_title || item.title,
url: item.url,
} );
} );
}

// Create Nav Block mapping attrs and Nav Items
return createBlock(
CORE_NAVIGATION_BLOCK_TYPE,
{
align: attributes.align,
itemsJustification: attributes.textAlign,
},
navItems
);
},
},
],
};