From c5c1c3b4e694570066ec14589d01453b19b1f104 Mon Sep 17 00:00:00 2001 From: Chris Huber Date: Sat, 26 Sep 2026 00:02:40 -0400 Subject: [PATCH 1/8] fix(php-transformer): recognize paragraph-labelled menus inside custom-element hosts A presentation-only custom element wrapping a nav was captured as companion HTML before NavigationPattern could see the landmark, so paragraph-wrapped menu labels never became editable navigation links. --- php-transformer/composer.json | 1 + .../src/HtmlToBlocks/HtmlCompilation.php | 34 ++++++++++ .../Patterns/NavigationPattern.php | 31 +++++++++ .../unit/paragraph-label-desktop-menu.php | 68 +++++++++++++++++++ 4 files changed, 134 insertions(+) create mode 100644 php-transformer/tests/unit/paragraph-label-desktop-menu.php diff --git a/php-transformer/composer.json b/php-transformer/composer.json index 40693eb1d..5a2708af0 100644 --- a/php-transformer/composer.json +++ b/php-transformer/composer.json @@ -229,6 +229,7 @@ "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/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..5fe314f90 100644 --- a/php-transformer/src/HtmlToBlocks/Patterns/NavigationPattern.php +++ b/php-transformer/src/HtmlToBlocks/Patterns/NavigationPattern.php @@ -52,6 +52,37 @@ 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 `