Add three icons, a styleable menu button and a reliable back-to-top in 1.13.0
CMS quality / php (8.2) (push) Canceled after 0s
CMS quality / php (8.4) (push) Canceled after 0s
CMS quality / browser (push) Canceled after 0s
CMS quality / mysql (push) Canceled after 0s

The icon set had no drawing for people, music or a house - the three pictures
the Tagespflege Moldovan template asks for. They join as `people`, `music` and
`home`; a test keeps the list and the drawings together, because a missing path
renders an empty `<path d="">`: invisible, without an error.

An icon block's own colour never reached its drawing: the embedded SVG carries
the same `block-icon` class as its wrapper and set the accent colour again.
`.block-icon .block-icon{color:inherit}` fixes it at the root.

The menu button becomes a thing you can shape. `burgerLabel` chooses symbol and
text, text only or symbol only (which keeps the name for screen readers), and
the new style group `menu` targets the button itself, the way `img` targets an
image and `controls` the slider. Inside the header the opened menu now lies
under the header across the full width instead of pushing the header apart -
on a phone that looked like a missing navigation.

The back-to-top button no longer depends on `animation-timeline: scroll()`. A
browser that parses the property but does not run the timeline left it in the
first frame of the animation: permanently invisible. Two classes from
scrolled.js decide now, and without a script the button simply stays visible.

Also: the Tagespflege Moldovan rebuild as its own installation, built only by
`scripts/seed-tagespflege-moldovan.php`.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
kantarciandClaude Opus 5 committed 2026-09-14 22:11:36 +02:00
1 parent 215ab064ae
commit 73719bae4a
19 files changed
+948 -15

No files matched your search

+78
View File
@@ -1887,6 +1887,29 @@ final class CmsTest extends TestCase
self::assertStringContainsString('Bewertung: 5 von 5', $html);
}
/**
* Jedes erlaubte Symbol braucht eine Zeichnung, und die Symbolfarbe muss am SVG ankommen.
*
* Ohne den ersten Teil rendert ein Name aus der Liste ein leeres `<path d="">`: unsichtbar,
* ohne Fehler, erst auf der fertigen Seite bemerkbar. Der zweite Teil sichert die Regel, die
* das eingebettete SVG die Farbe seiner Hülle erben lässt - es trägt dieselbe Klasse und
* setzte sonst die Akzentfarbe erneut.
*/
public function testEveryAllowedIconHasADrawingAndTakesItsColourFromTheBlock(): void
{
$template = (string) file_get_contents(__DIR__.'/../templates/blocks/icon.html.twig');
foreach (DocumentValidator::ICONS as $icon) {
self::assertMatchesRegularExpression(
'/
\s{4}'.preg_quote($icon, '/').": '[^']{4,}',?
/",
$template,
sprintf('Das Symbol „%s“ steht in der Liste, hat aber keine Zeichnung.', $icon),
);
}
self::assertStringContainsString('.block-icon .block-icon{color:inherit}', (string) file_get_contents(__DIR__.'/../public/assets/site.css'));
}
/**
* @return iterable<string, array{array}>
*/
@@ -3342,6 +3365,61 @@ final class CmsTest extends TestCase
self::assertStringContainsString("script-src 'none'", (string) $this->request('/')->headers->get('Content-Security-Policy'));
}
/** @return iterable<string, array{string, list<string>, list<string>}> */
public static function burgerLabels(): iterable
{
yield 'symbol and text' => ['both', ['<span class="burger-lines"', '<span class="burger-text">Menü'], []];
yield 'only text' => ['text', ['<span class="burger-text">Menü'], ['burger-lines']];
yield 'only the symbol, the name stays for screen readers' => ['icon', ['<span class="burger-lines"', '<span class="burger-text visually-hidden">Menü'], []];
}
/**
* @param list<string> $enthalten
* @param list<string> $fehlen
*/
#[DataProvider('burgerLabels')]
public function testTheMenuButtonCanBeSymbolTextOrBoth(string $variante, array $enthalten, array $fehlen): void
{
$document = DemoContent::starter('Mit Menü');
$document['schemaVersion'] = 16;
$document['blocks'][0]['children'][] = ['id' => 'nav', 'type' => 'menu', 'props' => ['slot' => 'header', 'layout' => 'burger', 'burgerLabel' => $variante]];
$this->menus->save('header', 'Hauptnavigation', [['label' => 'Start', 'type' => 'page', 'level' => 1, 'pageId' => 'page-1', 'position' => 10]], 0);
$this->pages->publish('page-1', $this->pages->saveDraft('page-1', $document, 0), 1);
// Nur der eigene Knopf, nicht der des Kopfbereichs aus der Startvorlage.
$crawler = new \Symfony\Component\DomCrawler\Crawler($this->request('/')->getContent());
$summary = $crawler->filter('#nav > details.menu-burger > summary')->html();
foreach ($enthalten as $teil) {
self::assertStringContainsString($teil, $summary);
}
foreach ($fehlen as $teil) {
self::assertStringNotContainsString($teil, $summary);
}
}
public function testAnInventedMenuButtonVariantIsRefused(): void
{
$document = DemoContent::starter('Erfunden');
$document['schemaVersion'] = 16;
$document['blocks'][0]['children'][] = ['id' => 'nav', 'type' => 'menu', 'props' => ['slot' => 'header', 'layout' => 'burger', 'burgerLabel' => 'gross']];
$this->expectException(InvalidDocument::class);
$this->pages->saveDraft('page-1', $document, 0);
}
/** Die Farben des Menü-Knopfs liegen am Block und müssen am summary ankommen. */
public function testTheMenuButtonTakesItsOwnColours(): void
{
$document = DemoContent::starter('Farbiger Knopf');
$document['schemaVersion'] = 16;
$document['blocks'][0]['children'][] = [
'id' => 'nav', 'type' => 'menu', 'props' => ['slot' => 'header', 'layout' => 'burger'],
'style' => ['desktop' => ['burgerColor' => '#294655', 'burgerBackgroundColor' => '#ffffff', 'burgerBorderColor' => '#d8e0d1', 'burgerBorderRadius' => '3px', 'burgerFontSize' => '14px']],
];
$this->menus->save('header', 'Hauptnavigation', [['label' => 'Start', 'type' => 'page', 'level' => 1, 'pageId' => 'page-1', 'position' => 10]], 0);
$this->pages->publish('page-1', $this->pages->saveDraft('page-1', $document, 0), 1);
$html = $this->request('/')->getContent();
self::assertStringContainsString('#nav .menu-burger>summary{color:#294655;background-color:#ffffff;border-color:#d8e0d1;border-radius:3px;font-size:14px}', $html);
}
public function testAMenuLayoutNeedsSchemaSixteen(): void
{
$document = DemoContent::starter('Zu alt');