From 1321a81aff8a5b5b6ec7ad24cd7192d76f318c1c Mon Sep 17 00:00:00 2001 From: Chris Huber Date: Sat, 1 Aug 2026 22:26:08 -0400 Subject: [PATCH] fix(transformer): preserve details open state AI assistance: gpt-5.6-sol via OpenCode; used to diagnose, implement, and test this change. --- .../src/HtmlToBlocks/BlockFactory.php | 2 +- .../HtmlToBlocks/Patterns/DetailsPattern.php | 3 ++- php-transformer/tests/contract/run.php | 18 ++++++++++++++++++ 3 files changed, 21 insertions(+), 2 deletions(-) diff --git a/php-transformer/src/HtmlToBlocks/BlockFactory.php b/php-transformer/src/HtmlToBlocks/BlockFactory.php index 184adfeb..c2980a7c 100644 --- a/php-transformer/src/HtmlToBlocks/BlockFactory.php +++ b/php-transformer/src/HtmlToBlocks/BlockFactory.php @@ -234,7 +234,7 @@ private function blockHtml(string $name, array $attrs, array $innerBlocks): stri if ( 'core/details' === $name ) { return array( - 'opening' => 'blockSupportAttrs($attrs, 'wp-block-details') . '>' . ($attrs['summary'] ?? '') . '', + 'opening' => 'blockSupportAttrs($attrs, 'wp-block-details') . ( ! empty($attrs['showContent']) ? ' open' : '' ) . '>' . ($attrs['summary'] ?? '') . '', 'closing' => '', ); } diff --git a/php-transformer/src/HtmlToBlocks/Patterns/DetailsPattern.php b/php-transformer/src/HtmlToBlocks/Patterns/DetailsPattern.php index 0be2b95f..2f7c3ab5 100644 --- a/php-transformer/src/HtmlToBlocks/Patterns/DetailsPattern.php +++ b/php-transformer/src/HtmlToBlocks/Patterns/DetailsPattern.php @@ -24,7 +24,8 @@ public function match(DOMElement $element, array &$fallbacks, callable $convertC } return $createBlock('core/details', array_filter(array_merge($presentationAttributes($element), array( - 'summary' => $summary instanceof DOMElement ? $innerHtml($summary) : '', + 'summary' => $summary instanceof DOMElement ? $innerHtml($summary) : '', + 'showContent' => $element->hasAttribute('open') ? true : '', )), static fn ($value): bool => '' !== $value), $children, $element); } diff --git a/php-transformer/tests/contract/run.php b/php-transformer/tests/contract/run.php index 1ae14799..720a6bcb 100644 --- a/php-transformer/tests/contract/run.php +++ b/php-transformer/tests/contract/run.php @@ -406,6 +406,24 @@ public function match(DOMElement $element, PatternContext $context): ?array $assert('Can I reschedule?' === ($detailsAccordionItems[0]['innerBlocks'][0]['attrs']['title'] ?? null), 'details summary text maps to accordion heading'); $assert('Yes, with notice.' === ($detailsAccordionItems[0]['innerBlocks'][1]['innerBlocks'][0]['attrs']['content'] ?? null), 'details body text maps to accordion panel'); +$openDetailsResult = ( new HtmlTransformer() )->transform('
Open summary

Open content.

')->toArray(); +$openDetailsBlock = $openDetailsResult['blocks'][0] ?? array(); +$openDetailsMarkup = (string) ($openDetailsResult['serialized_blocks'] ?? ''); +$assert('core/details' === ($openDetailsBlock['blockName'] ?? null), 'open native details converts to core/details'); +$assert(true === ($openDetailsBlock['attrs']['showContent'] ?? null), 'open native details maps to the core/details showContent attribute'); +$assert(str_contains($openDetailsMarkup, '
Open summary'), 'open native details serializes the frontend open attribute before its summary'); +$assert(strpos($openDetailsMarkup, 'Open summary') < strpos($openDetailsMarkup, '

Open content.

'), 'open native details preserves summary before content through final serialization'); +$assert('pass' === ($openDetailsResult['source_reports']['wp_block_validity']['status'] ?? ''), 'open native details serialization remains Gutenberg-valid'); + +$closedDetailsResult = ( new HtmlTransformer() )->transform('
Closed summary

Closed content.

')->toArray(); +$closedDetailsBlock = $closedDetailsResult['blocks'][0] ?? array(); +$closedDetailsMarkup = (string) ($closedDetailsResult['serialized_blocks'] ?? ''); +$assert('core/details' === ($closedDetailsBlock['blockName'] ?? null), 'closed native details converts to core/details'); +$assert(false === ($closedDetailsBlock['attrs']['showContent'] ?? false), 'closed native details keeps the core/details default closed state'); +$assert(str_contains($closedDetailsMarkup, '
Closed summary'), 'closed native details serializes without the frontend open attribute'); +$assert(strpos($closedDetailsMarkup, 'Closed summary') < strpos($closedDetailsMarkup, '

Closed content.

'), 'closed native details preserves summary before content through final serialization'); +$assert('pass' === ($closedDetailsResult['source_reports']['wp_block_validity']['status'] ?? ''), 'closed native details serialization remains Gutenberg-valid'); + // A single disclosure widget (toggle control + collapsible region) carries no // faq/accordion class, only the structural WAI-ARIA disclosure shape, and is // converted to a native zero-JS core/details block instead of leaking a dead