Skip to content
Merged
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
18 changes: 18 additions & 0 deletions php-transformer/src/HtmlToBlocks/HtmlTransformer.php
Original file line number Diff line number Diff line change
Expand Up @@ -391,6 +391,10 @@ private function collectSourceLandmarks(DOMElement $element, array &$counts, arr
private function landmarkKindForElement(DOMElement $element): string
{
$tagName = strtolower($element->tagName);
if ( 'footer' === $tagName && $this->hasAncestorTag($element, array( 'blockquote', 'figure' )) ) {
return '';
}

if ( in_array($tagName, array( 'header', 'nav', 'main', 'footer' ), true) ) {
return 'nav' === $tagName ? 'nav' : $tagName;
}
Expand All @@ -404,6 +408,20 @@ private function landmarkKindForElement(DOMElement $element): string
};
}

/**
* @param array<int, string> $tagNames
*/
private function hasAncestorTag(DOMElement $element, array $tagNames): bool
{
for ( $node = $element->parentNode; $node instanceof DOMElement && 'body' !== strtolower($node->tagName); $node = $node->parentNode ) {
if ( in_array(strtolower($node->tagName), $tagNames, true) ) {
return true;
}
}

return false;
}

/**
* @param array<int, array<string, mixed>> $blocks
* @param array<int, array<string, mixed>> $sourceProvenance
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,10 @@ private function navigationItemAttributes(DOMElement $item, DOMElement $anchor,
$itemAttrs = $item->isSameNode($anchor) ? array() : $presentationAttributes($item);
$anchorAttrs = $presentationAttributes($anchor);
$submenuAttrs = $submenuContainer instanceof DOMElement ? $presentationAttributes($submenuContainer) : array();
if ( '' === (string) ($itemAttrs['className'] ?? '') && '' !== (string) ($anchorAttrs['className'] ?? '') ) {
$itemAttrs['className'] = $anchorAttrs['className'];
}

return array_filter(array_merge($itemAttrs, $baseAttrs, array(
'anchorClassName' => $anchorAttrs['className'] ?? '',
'anchorStyle' => $anchorAttrs['style'] ?? '',
Expand Down
16 changes: 16 additions & 0 deletions php-transformer/tests/contract/run.php
Original file line number Diff line number Diff line change
Expand Up @@ -434,6 +434,15 @@ function serialize_blocks(array $blocks): string
$assert(str_contains($footerNavigationSerialized, 'footer-link'), 'footer navigation preserves link classes for styling and script targets');
$assert(str_contains($footerNavigationSerialized, 'social-link'), 'social navigation preserves social link classes for styling and script targets');

$runtimeTargetNavigation = ( new HtmlTransformer() )->transform(
'<nav aria-label="Docs"><ul><li><a class="nav-link" href="/guide">Guide</a></li></ul></nav>',
array('runtime_dom_selectors' => array('.nav-link'))
)->toArray();
$runtimeTargetNavigationSerialized = (string) ($runtimeTargetNavigation['serialized_blocks'] ?? '');
$runtimeTargetNavigationItemAttrs = $runtimeTargetNavigation['blocks'][0]['innerBlocks'][0]['attrs'] ?? array();
$assert('nav-link' === ($runtimeTargetNavigationItemAttrs['className'] ?? ''), 'runtime-target navigation link classes are preserved on navigation item attrs');
$assert(str_contains($runtimeTargetNavigationSerialized, '<li class="wp-block-navigation-item wp-block-navigation-link nav-link">'), 'runtime-target navigation link classes are exposed on navigation item markup');

$headerCluster = ( new HtmlTransformer() )->transform(
'<header class="site-header"><a class="site-logo" href="/">Acme Lab</a><nav class="primary-nav" aria-label="Primary"><a class="nav-link" href="/work">Work</a><a class="nav-link" href="/docs"><span>Docs</span></a></nav><form class="site-search" role="search" action="/search"><label for="q">Search</label><input id="q" type="search" name="q" placeholder="Search docs"><button type="submit">Search</button></form><div class="header-actions"><a class="cta" href="/start">Get started</a></div></header>'
)->toArray();
Expand All @@ -456,6 +465,13 @@ function serialize_blocks(array $blocks): string
$assert(1 === ($unmappedFinding['source_count'] ?? null), 'semantic parity missing landmark finding exposes source count');
$assert(0 === ($unmappedFinding['block_count'] ?? null), 'semantic parity missing landmark finding exposes generated block count');

$quoteCitationFooter = ( new HtmlTransformer() )->transform(
'<main><section><blockquote><p>Lovely dinner.</p><footer>Local Guide</footer></blockquote></section></main><footer>Restaurant footer</footer>'
)->toArray();
$quoteCitationParity = $quoteCitationFooter['source_reports']['semantic_parity'] ?? array();
$assert('pass' === ($quoteCitationParity['status'] ?? ''), 'blockquote citation footer is not counted as a page footer landmark');
$assert(1 === ($quoteCitationParity['landmarks']['source']['footer'] ?? null), 'semantic parity counts only the actual page footer landmark');

$assertNoInnerContentChildCountMismatch = static function (array $result, string $message) use ($assert): void {
$findingCodes = array_map(static fn (array $finding): string => (string) ($finding['code'] ?? ''), $result['source_reports']['wp_block_validity']['findings'] ?? array());
$assert(! in_array('inner_content_child_count_mismatch', $findingCodes, true), $message, implode(', ', $findingCodes));
Expand Down
Loading