Randlose Abschnitte, volle Bildschirmhoehe, Vollbild-Hero
Umuts Frage: "Haben wir bilder usw als volles bild startseite? 100% hoehe usw?" Die Teile gab es, die Kombination nicht - und zwei Sachen fehlten wirklich. Abschnittsbreiten: neu "randlos" (ohne Begrenzung, ohne Rand, von Kante zu Kante) und "breit" (1600 px) neben normal und schmal. Vorher war jeder Inhalt auf 1180 px begrenzt; ein Bild von Kante zu Kante war nicht moeglich. Hoeheneinheiten: neu dvh, svh, lvh. 100vh ist auf dem Handy hoeher als der Bildschirm - es meint die Ansicht ohne Adressleiste, und der untere Rand verschwindet darunter. 100dvh ist das, was jemand meint, der "volle Hoehe" sagt. Und eine Vorlage "Vollbild-Hero", die die ganze Kombination auf einen Klick setzt: randlos, 100dvh (mobil 80dvh), Inhalt senkrecht zentriert, Hintergrundbild formatfuellend, dunkle Ueberlagerung damit der Text lesbar bleibt, Ueberschrift, Text, Button. Die Teile waren alle da, aber diese Kombination zu finden war eine Recherche. 328 PHP-Tests / 1329 Assertions, 17 JS-Modelltests. Geprueft: vier Breiten angenommen und eine erfundene abgewiesen, sieben Hoehenangaben mit dvh/svh/lvh und zwei ungueltige, ein randloser Abschnitt mit formatfuellendem Bild, und die Hero-Vorlage Eigenschaft fuer Eigenschaft. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
464f52b147
commit
67b85751cf
11 files changed
+165
-7
No files matched your search
@@ -1776,6 +1776,83 @@ final class CmsTest extends TestCase
|
||||
]);
|
||||
}
|
||||
|
||||
/** @return iterable<string, array{string, bool}> */
|
||||
public static function sectionWidths(): iterable
|
||||
{
|
||||
yield 'normal' => ['normal', true];
|
||||
yield 'narrow' => ['narrow', true];
|
||||
yield 'wide' => ['wide', true];
|
||||
yield 'edge to edge' => ['full', true];
|
||||
yield 'invented' => ['riesig', false];
|
||||
}
|
||||
|
||||
#[DataProvider('sectionWidths')]
|
||||
public function testASectionCanRunEdgeToEdge(string $width, bool $accepted): void
|
||||
{
|
||||
$document = DemoContent::starter('Breite');
|
||||
$document['blocks'][0]['layout'] = ['tone' => 'paper', 'width' => $width];
|
||||
if (!$accepted) {
|
||||
$this->expectException(InvalidDocument::class);
|
||||
$this->pages->saveDraft('page-1', $document, 0);
|
||||
|
||||
return;
|
||||
}
|
||||
$this->pages->publish('page-1', $this->pages->saveDraft('page-1', $document, 0), 1);
|
||||
self::assertStringContainsString('width-'.$width, $this->request('/')->getContent());
|
||||
}
|
||||
|
||||
/** @return iterable<string, array{string, bool}> */
|
||||
public static function heights(): iterable
|
||||
{
|
||||
// 100vh is wrong on a phone - it means the viewport without the address bar. The unit
|
||||
// people actually want is dvh, so it has to be allowed.
|
||||
yield 'dynamic viewport height' => ['100dvh', true];
|
||||
yield 'small viewport height' => ['100svh', true];
|
||||
yield 'large viewport height' => ['100lvh', true];
|
||||
yield 'classic viewport height' => ['100vh', true];
|
||||
yield 'pixels' => ['640px', true];
|
||||
yield 'invented unit' => ['100qh', false];
|
||||
yield 'no unit' => ['100', false];
|
||||
}
|
||||
|
||||
#[DataProvider('heights')]
|
||||
public function testAFullHeightSectionIsPossibleWithTheUnitThatWorksOnPhones(string $height, bool $accepted): void
|
||||
{
|
||||
$document = DemoContent::starter('Hoch');
|
||||
$document['schemaVersion'] = 10;
|
||||
$document['blocks'][0]['style'] = ['desktop' => ['minHeight' => $height, 'display' => 'flex', 'alignItems' => 'center']];
|
||||
if (!$accepted) {
|
||||
$this->expectException(InvalidDocument::class);
|
||||
$this->pages->saveDraft('page-1', $document, 0);
|
||||
|
||||
return;
|
||||
}
|
||||
$this->pages->publish('page-1', $this->pages->saveDraft('page-1', $document, 0), 1);
|
||||
$html = $this->request('/')->getContent();
|
||||
self::assertStringContainsString('min-height:'.$height, $html);
|
||||
// Full height alone would put the content at the top; the flex properties centre it.
|
||||
self::assertStringContainsString('display:flex;align-items:center', $html);
|
||||
}
|
||||
|
||||
public function testAFullBleedImageFillsTheSection(): void
|
||||
{
|
||||
$document = DemoContent::starter('Bildfläche');
|
||||
$document['schemaVersion'] = 15;
|
||||
$document['blocks'][0]['layout'] = ['tone' => 'paper', 'width' => 'full'];
|
||||
$document['blocks'][0]['style'] = ['desktop' => ['padding' => ['top' => '0', 'bottom' => '0']]];
|
||||
$document['blocks'][0]['children'][] = [
|
||||
'id' => 'flaeche', 'type' => 'image',
|
||||
'props' => ['src' => '/assets/image-placeholder.svg', 'alt' => 'Weite Landschaft', 'decorative' => false, 'width' => 2400, 'height' => 1200],
|
||||
'style' => ['desktop' => ['width' => '100%', 'objectFit' => 'cover', 'imageHeight' => '100dvh']],
|
||||
];
|
||||
$this->pages->publish('page-1', $this->pages->saveDraft('page-1', $document, 0), 1);
|
||||
$html = $this->request('/')->getContent();
|
||||
self::assertStringContainsString('width-full', $html);
|
||||
self::assertStringContainsString('#flaeche{width:100%}', $html);
|
||||
// The height and the crop belong on the image itself, not on its figure.
|
||||
self::assertStringContainsString('#flaeche img{object-fit:cover;height:100dvh}', $html);
|
||||
}
|
||||
|
||||
/** @return iterable<string, array{string, array<string, mixed>, list<string>}> */
|
||||
public static function newBlocks(): iterable
|
||||
{
|
||||
|
||||
@@ -302,3 +302,26 @@ test('a row arrives with its columns, inside the selected container', () => {
|
||||
for (let i = 0; i < 4; i++) history.undo();
|
||||
assert.deepEqual(history.document, fixture());
|
||||
});
|
||||
|
||||
test('the hero preset assembles a full screen section that centres its content', () => {
|
||||
const history = new EditorHistory(fixture());
|
||||
const id = history.change(doc => insertPreset(doc, 'hero', 's1'));
|
||||
const hero = locate(history.document.blocks, id).block;
|
||||
|
||||
assert.equal(hero.type, 'section');
|
||||
assert.equal(hero.layout.width, 'full', 'Edge to edge, or it is not full screen.');
|
||||
// dvh, not vh: on a phone 100vh is taller than the screen.
|
||||
assert.equal(hero.style.desktop.minHeight, '100dvh');
|
||||
assert.equal(hero.style.mobile.minHeight, '80dvh');
|
||||
assert.equal(hero.style.desktop.display, 'flex');
|
||||
assert.equal(hero.style.desktop.alignItems, 'center');
|
||||
assert.equal(hero.style.desktop.backgroundSize, 'cover');
|
||||
assert.ok(hero.style.desktop.overlayOpacity > 0, 'An overlay, so text stays readable on a photo.');
|
||||
assert.deepEqual(hero.children.map(child => child.type), ['heading', 'text', 'button']);
|
||||
assert.equal(hero.children[0].props.level, 1);
|
||||
// Free styles need schema v10.
|
||||
assert.ok(history.document.schemaVersion >= 10);
|
||||
|
||||
history.undo();
|
||||
assert.deepEqual(history.document, fixture());
|
||||
});
|
||||
Reference in new issue
Block a user