diff --git a/php-transformer/src/HtmlToBlocks/HtmlTransformer.php b/php-transformer/src/HtmlToBlocks/HtmlTransformer.php index 13273532..7180c0d3 100644 --- a/php-transformer/src/HtmlToBlocks/HtmlTransformer.php +++ b/php-transformer/src/HtmlToBlocks/HtmlTransformer.php @@ -10190,6 +10190,14 @@ private function namePriceRowBlockFromElement(DOMElement $element, array &$fallb return null; } + // core/columns is a flex layout; WordPress rejects it with is-layout-grid. + // Decline when the resolved layout is grid so the container demotes to + // core/group, where grid layout is native. + $layout = $this->presentationAttributes($element)['layout'] ?? null; + if ( is_array($layout) && 'grid' === (string) ($layout['type'] ?? '') ) { + return null; + } + $rowFallbacks = array(); $columns = array(); foreach ( $children as $child ) { diff --git a/php-transformer/src/HtmlToBlocks/Patterns/ColumnsPattern.php b/php-transformer/src/HtmlToBlocks/Patterns/ColumnsPattern.php index 6c0f3ac7..7c120311 100644 --- a/php-transformer/src/HtmlToBlocks/Patterns/ColumnsPattern.php +++ b/php-transformer/src/HtmlToBlocks/Patterns/ColumnsPattern.php @@ -51,6 +51,17 @@ public function match( return null; } + // core/columns is a flex layout; WordPress rejects it with is-layout-grid. + // The layout resolver stamps layout:{type:grid} from signals this + // recognizer's style-based grid bail cannot see (grid-ish class names, + // class-resolved display:grid), so a would-be columns container whose + // layout resolves to grid must decline here and demote to core/group, + // where grid layout is native. + $layout = ( $presentationAttributes($element) )['layout'] ?? null; + if ( is_array($layout) && 'grid' === (string) ($layout['type'] ?? '') ) { + return null; + } + $elementChildren = array(); foreach ( $element->childNodes as $child ) { if ( XML_TEXT_NODE === $child->nodeType && '' === trim($child->textContent ?? '') ) { diff --git a/php-transformer/tests/fixtures/parity/html-split-layout-grid-class-demotes-to-group.json b/php-transformer/tests/fixtures/parity/html-split-layout-grid-class-demotes-to-group.json new file mode 100644 index 00000000..694c3c27 --- /dev/null +++ b/php-transformer/tests/fixtures/parity/html-split-layout-grid-class-demotes-to-group.json @@ -0,0 +1,32 @@ +{ + "schema": "blocks-engine/php-transformer/parity-fixture/v1", + "name": "html-split-layout-grid-class-demotes-to-group", + "description": "A two-child container whose class name trips both the split-layout columns heuristic (hero-grid) and the explicit grid class signal must become a core/group with grid layout, never core/columns. core/columns is a flex layout: pairing it with layout:{type:grid} serializes the wp-block-columns + is-layout-grid combo WordPress rejects.", + "source_reference": { + "repo": "php-transformer", + "path": "tests/fixtures/parity/html-split-layout-grid-class-demotes-to-group.json", + "notes": "Derived from a bakery hero where .hero-grid { display:grid } held exactly two panes (copy + plate figure); the split-layout heuristic claimed it as columns while the grid class name stamped is-layout-grid, producing an invalid block." + }, + "legacy_comparison": { + "skip": true, + "reason": "This upstream primitive fixture has no downstream legacy comparison." + }, + "operation": "html_transformer.transform", + "input": { + "content": "

Fresh Bread Daily

Baked every morning.

\"Bread\"/
" + }, + "expected_blocks": [ + { "path": "blocks.0", "name": "core/group", "attrs": { "className": "hero-grid", "layout": { "type": "grid" } } } + ], + "expected_fallbacks": [], + "expect": [ + { "path": "status", "assert": "equals", "value": "success" }, + { "path": "fallbacks", "assert": "count", "count": 0 }, + { "path": "blocks", "assert": "count", "count": 1 }, + { "path": "blocks.0.innerBlocks", "assert": "count", "count": 2 }, + { "path": "serialized_blocks", "assert": "contains", "value": "
" }, + { "path": "serialized_blocks", "assert": "contains", "value": "" }, + { "path": "serialized_blocks", "assert": "not_contains", "value": "wp:columns" }, + { "path": "serialized_blocks", "assert": "not_contains", "value": "wp-block-columns" } + ] +}