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
Original file line number Diff line number Diff line change
Expand Up @@ -284,8 +284,14 @@ public function registerNativeButtonStyleRule(
// width; an intrinsic one would shrink it to its label.
} elseif ( ! $hasAuthoredWidth && in_array(CssValueInspector::comparable((string) ($sourceDeclarations['display'] ?? '')), array( 'flex', 'inline-flex', 'inline-block', 'inline-grid', 'inline-table' ), true) ) {
if ( 'center' === $inheritedTextAlignment ) {
$outerWrapperDeclarations[] = 'width:100%';
$outerWrapperDeclarations[] = 'max-width:100%';
// A flex or grid item shrinks to its content unless told to
// fill. In block flow the wrapper already spans its line,
// inside the margins the control's rules may give it; a
// 100% width would push it past them and off center.
if ( ! $this->sourceParentIsBlockContainer($sourceControl) ) {
$outerWrapperDeclarations[] = 'width:100%';
$outerWrapperDeclarations[] = 'max-width:100%';
}
} else {
$outerWrapperDeclarations[] = 'width:max-content';
$outerWrapperDeclarations[] = 'max-width:100%';
Expand Down Expand Up @@ -817,6 +823,17 @@ private function sourceControlFillsFlowLine(DOMElement $sourceControl, array $so
return in_array($parentDisplay, array( '', 'block', 'flow-root', 'list-item' ), true);
}

private function sourceParentIsBlockContainer(DOMElement $sourceControl): bool
{
$parent = $sourceControl->parentNode;
if ( ! $parent instanceof DOMElement ) {
return false;
}
$display = CssValueInspector::comparable((string) ($this->styleResolver->cssDeclarations($this->styleResolver->specificityResolvedPresentationStyle($parent))['display'] ?? ''));

return in_array($display, array( '', 'block', 'flow-root', 'list-item', 'inline-block', 'table-cell' ), true);
}

private function sourceControlFillsGridCell(DOMElement $sourceControl, array $sourceDeclarations): bool
{
if ( ! in_array(CssValueInspector::comparable((string) ($sourceDeclarations['display'] ?? '')), array( 'flex', 'grid', 'table' ), true) ) {
Expand Down
10 changes: 10 additions & 0 deletions php-transformer/tests/fixtures/centered-button-margins.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<!doctype html>
<html><head><style>
body{margin:0;font-family:sans-serif;font-size:16px}
.amounts{margin:0;padding:24px;list-style:none;text-align:center}
.amounts li{display:inline-block;vertical-align:top}
.btn{display:inline-block;padding:12px 24px;border:2px solid #333;background:none;font-size:18px}
.amounts .btn{min-width:80px;margin-right:8px;margin-left:8px}
</style></head><body>
<main><ul class="amounts"><li><button class="btn" type="button">50</button></li><li><button class="btn" type="button">100</button></li></ul></main>
</body></html>
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
* `text-align` becomes a core/buttons flex wrapper whose default main-axis
* alignment is left. The inherited alignment must be restated as
* layout.justifyContent or the source's centering is lost. The restated
* justification only has room to act when the wrapper fills its parent, so
* justification only has room to act when the wrapper spans its line, so
* the generated wrapper rule must not shrink-wrap a centered wrapper.
*
* Regression for Automattic/blocks-engine#2150: the wrapper consulted only the
Expand Down Expand Up @@ -93,8 +93,8 @@
$centeredButtons
);

// The restated justification has no room to act on a shrink-wrapped wrapper:
// the generated wrapper rule must fill the parent instead, the way the source
// The restated justification has no room to act on a shrink-wrapped wrapper.
// In block flow the flex wrapper spans its line on its own, the way the source
// block the control sat in did, while the button itself still hugs its label.
$centeredMarker = $controlMarkerOf($centeredButtons);
$centeredCss = $cssFor($centeredOut);
Expand All @@ -103,11 +103,6 @@
'the centered control carries a generated control marker',
$centeredButtons
);
$assert(
1 === preg_match('/' . preg_quote($centeredMarker, '/') . '\.' . preg_quote($centeredMarker, '/') . '\.wp-block-buttons\{width:100%;max-width:100%\}/', $centeredCss),
'a centered inline control fills its parent buttons wrapper instead of shrink-wrapping it',
$centeredCss
);
$assert(
! preg_match('/' . preg_quote($centeredMarker, '/') . '\.' . preg_quote($centeredMarker, '/') . '\.wp-block-buttons\{width:max-content/', $centeredCss),
'the centered buttons wrapper is not pinned to max-content',
Expand Down
2 changes: 1 addition & 1 deletion php-transformer/tools/visual-parity/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
"test:navigation-cluster-control": "node tests/navigation-cluster-trailing-control.mjs",
"test:conditional-native-button": "node tests/conditional-native-button.mjs",
"test:declared-body": "node tests/declared-document-role-button.mjs",
"test": "node tests/smoke.mjs && node tests/blockquote-margin-reset.mjs && node tests/synthetic-inline-image-percentage.mjs && node tests/linked-flex-item-image.mjs && node tests/inline-block-item-spacing.mjs && node tests/exact-fit-inline-flow.mjs && node tests/layout-shell-editor-dom.mjs && node tests/layout-shell-editor-geometry.mjs && node tests/layout-shell-editor-selectability.mjs && node tests/custom-video-host-editor-geometry.mjs && node tests/issue-1493-reduced-public-fixture.mjs && node tests/responsive-document-variants.mjs && node tests/image-custom-host-promotion.mjs && node tests/empty-group-editor-height.mjs && node tests/button-height-ownership.mjs && node tests/button-padding-height.mjs && node tests/positioned-button-containing-block.mjs && node tests/positioned-button-translate-once.mjs && node tests/positioned-button-shrink-to-fit.mjs && node tests/inline-control-line-box.mjs && node tests/flex-button-row-shrink-to-fit.mjs && node tests/navigation-layout-default.mjs && node tests/navigation-hover-focus-projection.mjs && node tests/navigation-overlay-duplicate-collapse.mjs && node tests/navigation-responsive-desktop-justification.mjs && node tests/navigation-overlay-source-breakpoint.mjs && node tests/navigation-open-button-source-placement.mjs && node tests/form-row-two-column-geometry.mjs && node tests/color-scheme-projection.mjs && node tests/shared-shell-route-cascade.mjs && node tests/responsive-counterpart-text-sync.mjs && node tests/positioned-carousel-visibility.mjs && node tests/authored-carousel-source-scope.mjs && node tests/empty-overlay-opacity.mjs && node tests/authored-carousel-stage-geometry.mjs && node tests/gallery-stage-controls.mjs && node tests/nested-responsive-font-size.mjs && node tests/responsive-layered-block-margin.mjs && node tests/svg-checkable-control.mjs && node tests/source-root-includes.mjs && node tests/captured-choice-group-runtime.mjs && node tests/differential-corpus.mjs",
"test": "node tests/smoke.mjs && node tests/blockquote-margin-reset.mjs && node tests/synthetic-inline-image-percentage.mjs && node tests/linked-flex-item-image.mjs && node tests/centered-button-margins.mjs && node tests/inline-block-item-spacing.mjs && node tests/exact-fit-inline-flow.mjs && node tests/layout-shell-editor-dom.mjs && node tests/layout-shell-editor-geometry.mjs && node tests/layout-shell-editor-selectability.mjs && node tests/custom-video-host-editor-geometry.mjs && node tests/issue-1493-reduced-public-fixture.mjs && node tests/responsive-document-variants.mjs && node tests/image-custom-host-promotion.mjs && node tests/empty-group-editor-height.mjs && node tests/button-height-ownership.mjs && node tests/button-padding-height.mjs && node tests/positioned-button-containing-block.mjs && node tests/positioned-button-translate-once.mjs && node tests/positioned-button-shrink-to-fit.mjs && node tests/inline-control-line-box.mjs && node tests/flex-button-row-shrink-to-fit.mjs && node tests/navigation-layout-default.mjs && node tests/navigation-hover-focus-projection.mjs && node tests/navigation-overlay-duplicate-collapse.mjs && node tests/navigation-responsive-desktop-justification.mjs && node tests/navigation-overlay-source-breakpoint.mjs && node tests/navigation-open-button-source-placement.mjs && node tests/form-row-two-column-geometry.mjs && node tests/color-scheme-projection.mjs && node tests/shared-shell-route-cascade.mjs && node tests/responsive-counterpart-text-sync.mjs && node tests/positioned-carousel-visibility.mjs && node tests/authored-carousel-source-scope.mjs && node tests/empty-overlay-opacity.mjs && node tests/authored-carousel-stage-geometry.mjs && node tests/gallery-stage-controls.mjs && node tests/nested-responsive-font-size.mjs && node tests/responsive-layered-block-margin.mjs && node tests/svg-checkable-control.mjs && node tests/source-root-includes.mjs && node tests/captured-choice-group-runtime.mjs && node tests/differential-corpus.mjs",
"test:differential": "node tests/differential-corpus.mjs"
},
"devDependencies": {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import assert from 'node:assert/strict';
import { readFileSync } from 'node:fs';
import { execFileSync } from 'node:child_process';
import { chromium } from 'playwright';

// A centered inline-block control with side margins keeps those margins
// inside its block-flow item: the item is as wide as in the source and the
// control sits at the same offset within it.
const root = new URL('../../..', import.meta.url).pathname;
const source = readFileSync(`${root}/tests/fixtures/centered-button-margins.html`, 'utf8');
const result = JSON.parse(execFileSync('php', ['-r', 'require $argv[1] . "/vendor/autoload.php"; echo json_encode((new Automattic\\BlocksEngine\\PhpTransformer\\HtmlToBlocks\\HtmlTransformer())->transform(base64_decode($argv[2]))->toArray());', root, Buffer.from(source).toString('base64')], { encoding: 'utf8' }));
const css = result.assets.filter(asset => asset.kind === 'css').map(asset => asset.content).join('\n');
// WordPress renders a flex layout's justifyContent as a justification class.
const blocks = result.serialized_blocks.replace(/<!-- wp:buttons (\{.*?\}) -->\s*<div class="wp-block-buttons/g, (match, attrs) => {
const justify = JSON.parse(attrs).layout?.justifyContent;
return justify ? match.replace('class="wp-block-buttons', `class="wp-block-buttons is-content-justification-${justify}`) : match;
});
const target = `<!doctype html><style>.wp-block-buttons{display:flex;flex-wrap:wrap}.wp-block-buttons.is-content-justification-center{justify-content:center}\n${css}</style>${blocks}`;
const browser = await chromium.launch({ headless: true });
try {
const measurements = [];
for (const html of [source, target]) {
const page = await browser.newPage({ viewport: { width: 1024, height: 600 } });
await page.setContent(html);
measurements.push(await page.evaluate(() => [...document.querySelectorAll('li')].map(item => {
const box = item.getBoundingClientRect();
return [Math.round(box.width), Math.round(item.querySelector('button').getBoundingClientRect().x - box.x)];
})));
await page.close();
}
console.log(JSON.stringify({ source: measurements[0], converted: measurements[1] }));
assert.deepEqual(measurements[1], measurements[0]);
} finally {
await browser.close();
}
Loading