diff --git a/php-transformer/composer.json b/php-transformer/composer.json index 40693eb1d..f6aa1c4c5 100644 --- a/php-transformer/composer.json +++ b/php-transformer/composer.json @@ -229,6 +229,8 @@ "php tests/unit/navigation-overlay-duplicate-collapse.php", "php tests/unit/layout-boundary-excludes-shell.php", "php tests/unit/navigation-nested-authored-anchor.php", + "php tests/unit/paragraph-label-desktop-menu.php", + "php tests/unit/shared-variant-navigation.php", "php tests/unit/button-signal-classifier.php", "php tests/unit/pill-button-anchor.php", "php tests/unit/button-style-resolver.php", diff --git a/php-transformer/src/HtmlToBlocks/HtmlCompilation.php b/php-transformer/src/HtmlToBlocks/HtmlCompilation.php index aa5b04fa0..667b5fa98 100644 --- a/php-transformer/src/HtmlToBlocks/HtmlCompilation.php +++ b/php-transformer/src/HtmlToBlocks/HtmlCompilation.php @@ -3189,6 +3189,14 @@ private function convertElement(DOMElement $element, array &$fallbacks, bool $ca return $carousel; } + // A custom-element menu host is not a component. Recognize its landmark + // before generated-component capture freezes paragraph-wrapped labels + // into a companion content attribute. + $navigationHost = $this->recognizedNavigationHostBlock($element, $fallbacks); + if ( null !== $navigationHost ) { + return $navigationHost; + } + if ( $this->isGeneratedComponentCandidate($element) ) { $generated = $this->fallbackEmitter()->maybeGenerateCustomBlock($element, $this->generatedBlocks(), true, true); if ( null !== $generated ) { @@ -3318,6 +3326,32 @@ private function linkBearingElementBlock(DOMElement $element): ?array ); } + /** + * Lower a custom-element host of a navigation landmark to that landmark's + * native navigation, keeping host identity CSS can still address. + * + * @param array> $fallbacks + * @return array|null + */ + private function recognizedNavigationHostBlock(DOMElement $element, array &$fallbacks): ?array + { + $landmark = ( new NavigationPattern() )->hostedNavigationLandmark($element); + if ( ! $landmark instanceof DOMElement ) { + return null; + } + + $navigation = $this->recognizePatterns($landmark, $fallbacks, array( AccordionPattern::class, SocialLinksPattern::class, NavigationPattern::class )); + if ( null === $navigation ) { + return null; + } + + if ( '' === trim($this->attr($element, 'id') . $this->attr($element, 'class') . $this->attr($element, 'style')) ) { + return $navigation; + } + + return $this->layoutShellBlockForElements(array( $element ), array( $navigation ), $element); + } + /** * Preserve a static source wrapper chain around editable inner blocks. * diff --git a/php-transformer/src/HtmlToBlocks/Patterns/NavigationPattern.php b/php-transformer/src/HtmlToBlocks/Patterns/NavigationPattern.php index 9e1ec63e8..39bdd7dbf 100644 --- a/php-transformer/src/HtmlToBlocks/Patterns/NavigationPattern.php +++ b/php-transformer/src/HtmlToBlocks/Patterns/NavigationPattern.php @@ -3,6 +3,7 @@ namespace Automattic\BlocksEngine\PhpTransformer\HtmlToBlocks\Patterns; +use Automattic\BlocksEngine\PhpTransformer\Css\CssIdent; use Automattic\BlocksEngine\PhpTransformer\Css\CssValueSplitter; use Automattic\BlocksEngine\PhpTransformer\HtmlToBlocks\Classification\MenuVocabulary; use Automattic\BlocksEngine\PhpTransformer\HtmlToBlocks\Style\StyleAttributeMapper; @@ -52,6 +53,74 @@ public function claimsBeforeAuthorOwnedLayout(DOMElement $element): bool return $this->hasHeaderLinkCluster($element) || $this->hasRepeatedLinkItems($element); } + /** + * A custom-element host whose only element child is a navigation landmark. + * + * Builders wrap an ordinary `