Add the supporting-pane canonical layout

Plan step 35: <x-supporting-pane>, following the reference's placement
table - below the focus pane below expanded, beside it from 840px at a
fixed 360px (412px from large) or the two-thirds split. Below expanded
compact="sheet" docks the supporting pane as a bottom sheet whose drag
handle opens it; always on the trailing side, so focus order matches the
panes on screen, and mirrored in RTL.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
This commit is contained in:
Andreas Reinhold / reini
2026-09-14 14:51:22 +02:00
co-authored by Claude Opus 5
parent b5c5be1fd7
commit 9eb06323aa
7 changed files with 386 additions and 0 deletions
+74
View File
@@ -312,3 +312,77 @@ it('selects from Alpine through x-model, with the layout\'s own back row', funct
->assertScript(listDetailVisible(LIST_PANE))
->assertScript("document.activeElement === document.querySelector('#pick')");
});
const SUPPORTING_MAIN = '[data-md-supporting-pane-main]';
const SUPPORTING_SIDE = '[data-md-supporting-pane-supporting]';
function supportingPanePage(int $width, string $attributes = '', string $dir = 'ltr'): mixed
{
$body = <<<BLADE
<x-supporting-pane label="Comments" {$attributes}>
<x-slot:main><article id="main" style="block-size: 1400px">The proposal</article></x-slot:main>
<x-slot:supporting><p id="comment">Friday works for me.</p><button type="button" id="reply">Reply</button></x-slot:supporting>
</x-supporting-pane>
BLADE;
return layoutPage($body, $width, 800, $dir);
}
it('puts the supporting pane under the focus pane below expanded', function () {
foreach ([599 => 16, 600 => 24, 839 => 24] as $width => $margin) {
supportingPanePage($width)
->assertScript(layoutRect(SUPPORTING_SIDE, 'top').' >= '.layoutRect(SUPPORTING_MAIN, 'bottom'))
->assertScript(layoutRect(SUPPORTING_SIDE, 'width').' === '.layoutRect(SUPPORTING_MAIN, 'width'))
->assertScript(layoutRect(SUPPORTING_MAIN, 'left')." === {$margin}")
->assertScript(layoutRect(SUPPORTING_SIDE, 'top').' - '.layoutRect(SUPPORTING_MAIN, 'bottom')." === {$margin}");
}
});
it('puts the supporting pane beside the focus pane from expanded, at M3\'s widths or the two-thirds split', function () {
foreach ([840 => 360, 1199 => 360, 1200 => 412] as $width => $side) {
supportingPanePage($width)
->assertScript(layoutRect(SUPPORTING_SIDE, 'top').' === '.layoutRect(SUPPORTING_MAIN, 'top'))
->assertScript(layoutRect(SUPPORTING_SIDE, 'width')." === {$side}")
->assertScript(layoutRect(SUPPORTING_SIDE, 'right')." === {$width} - 24")
->assertScript(layoutRect(SUPPORTING_SIDE, 'left').' - '.layoutRect(SUPPORTING_MAIN, 'right').' === 24');
}
supportingPanePage(1200, 'width="split"')
->assertScript('Math.abs('.layoutRect(SUPPORTING_MAIN, 'width').' - 2 * '.layoutRect(SUPPORTING_SIDE, 'width').') <= 1');
supportingPanePage(1200, '', 'rtl')
->assertScript(layoutRect(SUPPORTING_SIDE, 'left').' === 24')
->assertScript(layoutRect(SUPPORTING_SIDE, 'right').' < '.layoutRect(SUPPORTING_MAIN, 'left'));
});
it('docks the supporting pane as a bottom sheet below expanded, opened and closed by its handle', function () {
$handle = '[data-md-supporting-pane-handle]';
$page = supportingPanePage(599, 'compact="sheet"')
->assertScript("document.querySelector('{$handle}').checkVisibility()")
->assertAttribute($handle, 'aria-expanded', 'false')
->assertScript("! document.querySelector('#comment').checkVisibility()")
// Docked to the bottom of the window while the focus pane scrolls under it.
->assertScript(layoutRect(SUPPORTING_SIDE, 'bottom').' === window.innerHeight')
->assertScript(layoutStyle(SUPPORTING_SIDE, 'borderTopLeftRadius')." === '28px'");
$page->click($handle)
->assertAttribute($handle, 'aria-expanded', 'true')
->assertScript("document.querySelector('#comment').checkVisibility()")
->assertScript(layoutRect(SUPPORTING_SIDE, 'bottom').' === window.innerHeight');
$page->keys('#reply', 'Escape')
->assertAttribute($handle, 'aria-expanded', 'false')
->assertScript("! document.querySelector('#comment').checkVisibility()")
->assertScript("document.activeElement === document.querySelector('{$handle}')");
// Scrolled to the end, the sheet has its own room and covers nothing of the focus pane.
$page->script('window.scrollTo(0, document.documentElement.scrollHeight)');
$page->assertScript(layoutRect(SUPPORTING_SIDE, 'top').' >= '.layoutRect(SUPPORTING_MAIN, 'bottom'));
supportingPanePage(840, 'compact="sheet"')
->assertScript("! document.querySelector('{$handle}').checkVisibility()")
->assertScript("document.querySelector('#comment').checkVisibility()")
->assertScript(layoutRect(SUPPORTING_SIDE, 'width').' === 360')
->assertNoJavaScriptErrors();
});