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
8 changes: 8 additions & 0 deletions php-transformer/src/HtmlToBlocks/HtmlCompilation.php
Original file line number Diff line number Diff line change
Expand Up @@ -1994,6 +1994,14 @@ private function materializeAuthorStylesheet(string $html, string $staticCss, bo
$beforeAuthorCssParts[] = ':root :where(.' . self::CSS_OWNED_LAYOUT_CLASS . ')>.block-editor-inner-blocks,'
. ':root :where(.' . self::CSS_OWNED_LAYOUT_CLASS . ')>.block-editor-inner-blocks>.block-editor-block-list__layout{display:contents}';
}
$layoutShellBlockName = $this->generatedBlocks()->blockName('layout-shell');
if ( str_contains($serializedBlocks, '<!-- wp:' . $layoutShellBlockName) ) {
// A layout shell preserves the source wrapper chain. Gutenberg's
// InnerBlocks wrappers must not become grid or flex items within it.
$layoutShellClass = 'wp-block-' . str_replace('/', '-', $layoutShellBlockName);
$beforeAuthorCssParts[] = ':root :where(.' . $layoutShellClass . ')>.block-editor-inner-blocks,'
. ':root :where(.' . $layoutShellClass . ')>.block-editor-inner-blocks>.block-editor-block-list__layout{display:contents}';
}
if ( str_contains($serializedBlocks, self::CSS_OWNED_FLOW_CLASS) ) {
$beforeAuthorCssParts[] = ':root :where(.' . self::CSS_OWNED_FLOW_CLASS . '>p){margin-top:0;margin-bottom:0}';
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -242,17 +242,9 @@ private function animationConfiguration(array $declarations): array
$delay = $this->timeSeconds(strtolower(trim((string) (CssValueSplitter::splitTopLevel($declarations['animation-delay'], array( ',' ))[0] ?? '')))) ?? $delay;
}

// A scroll-driven timeline has no document clock behind it. Whether it
// ever advances depends on a scroll container the conversion does not
// control, and on this import it does not: `getAnimations()` reports the
// animation running while its progress — and the element's opacity —
// stays at zero.
$timeline = strtolower(trim((string) ($declarations['animation-timeline'] ?? '')));
$timelineDriven = '' !== $timeline && ! in_array($timeline, array( 'auto', 'none', 'initial', 'inherit', 'unset', 'revert', 'revert-layer' ), true);

return array(
'names' => $names,
'suspended' => $paused || $timelineDriven,
'suspended' => $paused,
// The before phase is what the element shows either because the fill
// mode paints it there, or because a non-positive delay leaves the
// stalled animation inside its active phase at time zero.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,12 @@
"expect": [
{ "path": "status", "assert": "equals", "value": "success" },
{ "path": "assets", "assert": "count", "count": 2 },
{ "path": "assets.0.path", "assert": "equals", "value": "assets/css/engine-support-before-author-d34877b98a167d1b.css" },
{ "path": "assets.0.path", "assert": "equals", "value": "assets/css/engine-support-before-author-63874c2d7aa4cc26.css" },
{ "path": "assets.0.source", "assert": "equals", "value": "engine-support" },
{ "path": "assets.0.stylesheet_placement", "assert": "equals", "value": "before-author" },
{ "path": "assets.0.content", "assert": "contains", "value": ":root :where(.blocks-engine-css-owned-grid)>*{margin-block-start:0;margin-block-end:0}" },
{ "path": "assets.0.content", "assert": "contains", "value": ":root :where(.blocks-engine-css-owned-layout)>.block-editor-inner-blocks>.block-editor-block-list__layout{display:contents}" },
{ "path": "assets.0.content", "assert": "contains", "value": ":root :where(.wp-block-custom-layout-shell)>.block-editor-inner-blocks>.block-editor-block-list__layout{display:contents}" },
{ "path": "assets.1.path", "assert": "equals", "value": "index.inline.css" },
{ "path": "assets.1.kind", "assert": "equals", "value": "css" },
{ "path": "assets.1.role", "assert": "equals", "value": "stylesheet" },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,12 @@
"expect": [
{ "path": "status", "assert": "equals", "value": "success" },
{ "path": "assets", "assert": "count", "count": 2 },
{ "path": "assets.0.path", "assert": "equals", "value": "assets/css/engine-support-before-author-d34877b98a167d1b.css" },
{ "path": "assets.0.path", "assert": "equals", "value": "assets/css/engine-support-before-author-63874c2d7aa4cc26.css" },
{ "path": "assets.0.source", "assert": "equals", "value": "engine-support" },
{ "path": "assets.0.stylesheet_placement", "assert": "equals", "value": "before-author" },
{ "path": "assets.0.content", "assert": "contains", "value": ":root :where(.blocks-engine-css-owned-grid)>*{margin-block-start:0;margin-block-end:0}" },
{ "path": "assets.0.content", "assert": "contains", "value": ":root :where(.blocks-engine-css-owned-layout)>.block-editor-inner-blocks>.block-editor-block-list__layout{display:contents}" },
{ "path": "assets.0.content", "assert": "contains", "value": ":root :where(.wp-block-custom-layout-shell)>.block-editor-inner-blocks>.block-editor-block-list__layout{display:contents}" },
{ "path": "assets.1.path", "assert": "equals", "value": "index.inline.css" },
{ "path": "assets.1.kind", "assert": "equals", "value": "css" },
{ "path": "assets.1.role", "assert": "equals", "value": "stylesheet" },
Expand Down
2 changes: 2 additions & 0 deletions php-transformer/tests/unit/custom-block-generator.php
Original file line number Diff line number Diff line change
Expand Up @@ -174,10 +174,12 @@
$shellDefinitions = array_values(array_filter($shellResult['source_reports']['generated_blocks'] ?? array(), static fn (array $definition): bool => 'Layout Shell' === ($definition['block_json']['title'] ?? null)));
$assert(str_ends_with((string) ($shellBlock['blockName'] ?? ''), '/layout-shell') && 8 === count($shellBlock['attrs']['wrappers'] ?? array()) && 'core/paragraph' === ($shellBlock['innerBlocks'][0]['blockName'] ?? null), '6: a projected wrapper chain becomes one layout-shell block around native editable content');
$shellScript = (string) ($shellDefinitions[0]['assets']['index.js'] ?? '');
$shellCss = implode("\n", array_map(static fn (array $asset): string => 'css' === ($asset['kind'] ?? '') ? (string) ($asset['content'] ?? '') : '', $shellResult['assets'] ?? array()));
$assert(1 === count($shellDefinitions) && str_contains($shellScript, 'InnerBlocks.Content'), '6: layout-shell emits one companion definition whose save path retains native inner blocks');
$assert(str_contains($shellScript, 'function wrappedContent( wrappers, content, outerProps )') && str_contains($shellScript, 'props = outerProps( props )') && str_contains($shellScript, 'content = wrappedContent( wrappers, content )') && str_contains($shellScript, 'edit: edit,'), '6: layout-shell edit preserves the save wrapper chain without merging editor props into authored wrappers');
$assert(str_contains($shellScript, "useBlockProps( { style: { display: 'contents' } } )"), '6: layout-shell uses a box-neutral editor carrier so Gutenberg layout CSS cannot override authored wrapper positioning');
$assert(str_contains($shellScript, "return createElement( 'div', useBlockProps( { style: { display: 'contents' } } ), content )"), '6: layout-shell retains the editor carrier for empty and source-backed wrapper chains');
$assert(str_contains($shellCss, ':root :where(.wp-block-custom-layout-shell)>.block-editor-inner-blocks,:root :where(.wp-block-custom-layout-shell)>.block-editor-inner-blocks>.block-editor-block-list__layout{display:contents}'), '6: layout-shell keeps its Gutenberg InnerBlocks wrappers out of the preserved source layout topology');
$assert(str_contains($shellScript, '__experimentalLabel: function( attributes, options )') && str_contains($shellScript, "context === 'list-view' || context === 'breadcrumb'") && str_contains($shellScript, "replace( /^_+/, '' ).replace( /_[a-z0-9]{5,}_\\d+$/i, '' )") && str_contains($shellScript, "return semantic + ': ' + detail") && str_contains($shellScript, "return 'Layout shell ('"), '6: layout-shell exposes concise semantic labels in List View and breadcrumbs');
$assert(false === ($shellDefinitions[0]['block_json']['supports']['renaming'] ?? true), '6: generated semantic labels are read-only rather than persisted as user metadata');
$assert(2 === ($shellResult['source_reports']['editability_report']['metrics']['max_nesting_depth'] ?? PHP_INT_MAX) && 8 === substr_count((string) ($shellResult['serialized_blocks'] ?? ''), 'id="shell-'), '6: layout-shell collapses List View depth while preserving every rendered source wrapper');
Expand Down
30 changes: 9 additions & 21 deletions php-transformer/tests/unit/reveal-animation-settling.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,9 @@
* A captured reveal must never leave content less visible than its end state.
*
* Wix pauses an entrance animation (`animation: motion-fadeIn … backwards paused`)
* behind a runtime attribute, and capture can hand the same animation to a
* scroll-driven timeline (`animation-timeline: view()`). Both are carried by
* import while the driver that would finish them is not, and
* `animation-fill-mode: backwards` then pins the element to the `0% {opacity:0}`
* keyframe forever: the content is in the block markup and never paints (#239).
* behind a runtime attribute. That runtime driver is absent after import, so
* `animation-fill-mode: backwards` pins the element to `opacity:0`. A running
* scroll timeline remains browser-driven and must stay authored.
*
* The repair replaces an animation that cannot progress with the resolved state
* it was travelling towards. An animation that can still run, and one whose start
Expand Down Expand Up @@ -46,8 +44,8 @@

$scrollDriven = $fadeIn . '@supports (animation-timeline: view()){#comp-k3o4lijt{animation:motion-fadeIn 1200ms 1100ms cubic-bezier(0.445, 0.05, 0.55, 0.95) backwards 1;animation-composition:replace;animation-play-state:running;animation-timeline:view();animation-range:entry 0% cover 40%}}';
$assert(
array( ':root #comp-k3o4lijt{animation:none!important;opacity:var(--comp-opacity, 1)!important}' ) === $settler->settleRules($scrollDriven),
'a scroll-driven entrance animation settles rather than resting on an unadvanced timeline',
array() === $settler->settleRules($scrollDriven),
'a running scroll-driven entrance animation keeps its browser-native timeline',
implode(' | ', $settler->settleRules($scrollDriven))
);

Expand Down Expand Up @@ -138,13 +136,8 @@
};

$afterAuthor = $collect($assets, static fn (array $asset): bool => 'engine-support' === ($asset['source'] ?? '') && 'after-author' === ($asset['stylesheet_placement'] ?? ''));
foreach ( array( 'comp-k3nod418', 'comp-k3jrzme8' ) as $id ) {
$assert(
str_contains($afterAuthor, ':root #' . $id . '{animation:none!important;opacity:var(--comp-opacity, 1)!important}'),
'the theme stylesheet settles #' . $id . ' after the author CSS that hides it',
$afterAuthor
);
}
$assert(str_contains($afterAuthor, ':root #comp-k3nod418{animation:none!important;opacity:var(--comp-opacity, 1)!important}'), 'the theme stylesheet settles the paused reveal after author CSS', $afterAuthor);
$assert(! str_contains($afterAuthor, ':root #comp-k3jrzme8{animation:none!important'), 'the theme stylesheet preserves the running scroll-timeline reveal', $afterAuthor);

// The artifact pipeline materializes the author stylesheets itself and hands the
// transform the projected copies, so the repair has to read those rather than the
Expand All @@ -157,13 +150,8 @@
is_array($pipelineResult['assets'] ?? null) ? $pipelineResult['assets'] : array(),
static fn (array $asset): bool => 'engine-support' === ($asset['source'] ?? '') && 'after-author' === ($asset['stylesheet_placement'] ?? '')
);
foreach ( array( 'comp-k3nod418', 'comp-k3jrzme8' ) as $id ) {
$assert(
str_contains($pipelineAfterAuthor, ':root #' . $id . '{animation:none!important;opacity:var(--comp-opacity, 1)!important}'),
'a pipeline transform that materializes its own author stylesheets still settles #' . $id,
$pipelineAfterAuthor
);
}
$assert(str_contains($pipelineAfterAuthor, ':root #comp-k3nod418{animation:none!important;opacity:var(--comp-opacity, 1)!important}'), 'a pipeline transform settles the paused reveal from its materialized author stylesheet', $pipelineAfterAuthor);
$assert(! str_contains($pipelineAfterAuthor, ':root #comp-k3jrzme8{animation:none!important'), 'a pipeline transform preserves the running scroll timeline from its materialized author stylesheet', $pipelineAfterAuthor);

$editorStaticState = $collect($assets, static fn (array $asset): bool => 'editor-static-state' === ($asset['source'] ?? ''));
$assert(
Expand Down
Loading