Files
livewire-material/tests/Feature/Components/GridTest.php
T
Andreas Reinhold / reiniandClaude Sonnet 5 925d9a4439 Fold layout.css and components.css into all.css
Plan step 37: all.css replaces the two interim import lists with one
entry for an application that wants everything — the foundation, every
layout stylesheet and every component stylesheet, grouped under the
same block comments components.css used, plus a Layout block. It also
directly imports the three files nothing imported by name before
(layout/spacing.css, layout/visibility.css, components/selection.css),
so every file under components/ and layout/ is now one @import away.

Every test that read a block of components.css or layout.css now reads
the matching block of all.css through one shared helper (allCssBlock(),
in tests/Pest.php so it loads for any test run) instead of repeating
the same substr() search in each file. StylesheetsTest.php's shape,
Tailwind-free and breakpoint checks, previously run twice (once for
the foundation, once for the layout tree), now run once over the whole
tree all.css reaches, since every component and layout stylesheet is
plain CSS after step 36; a new test asserts all.css imports everything
under components/ and layout/ exactly once. The Workbench imports
all.css in place of layout.css and components.css.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
2026-09-15 02:49:29 +02:00

80 lines
4.5 KiB
PHP

<?php
use NoNameWeb\LivewireMaterial\Support\Layout;
/**
* A grid's column properties from its inline style, by breakpoint.
*
* @return array<string, string>
*/
function gridColumns(string $html): array
{
preg_match_all('/--md-columns-([a-z-]+): (\d+);/', layoutRoot($html)['style'] ?? '', $matches, PREG_SET_ORDER);
return collect($matches)->mapWithKeys(fn (array $match): array => [$match[1] => $match[2]])->all();
}
it('writes all five column counts on every grid, one column when given none', function () {
$html = (string) $this->blade('<x-grid><div>One</div></x-grid>');
expect(layoutRoot($html))->toMatchArray(['<' => 'div', 'data-md-grid' => 'data-md-grid'])
->not->toHaveKeys(['data-md-gap', 'data-md-min-item', 'data-md-columns'])
->and(gridColumns($html))->toBe(['compact' => '1', 'medium' => '1', 'expanded' => '1', 'large' => '1', 'extra-large' => '1']);
});
it('fills each breakpoint left out from the nearest smaller one', function () {
expect(gridColumns((string) $this->blade('<x-grid :columns="[\'medium\' => 2, \'large\' => 4]" />')))
->toBe(['compact' => '1', 'medium' => '2', 'expanded' => '2', 'large' => '4', 'extra-large' => '4'])
->and(gridColumns((string) $this->blade('<x-grid :columns="3" />')))
->toBe(['compact' => '3', 'medium' => '3', 'expanded' => '3', 'large' => '3', 'extra-large' => '3'])
// A count that is not a whole number from 1 keeps the one before it.
->and(gridColumns((string) $this->blade('<x-grid :columns="[\'compact\' => 2, \'medium\' => 0, \'expanded\' => \'many\', \'large\' => 5, \'huge\' => 9]" />')))
->toBe(['compact' => '2', 'medium' => '2', 'expanded' => '2', 'large' => '5', 'extra-large' => '5']);
expect(Layout::columns(['extra-large' => 6]))->toBe(['compact' => 1, 'medium' => 1, 'expanded' => 1, 'large' => 1, 'extra-large' => 6]);
});
it('spaces the columns with a spacing token only, and with none for anything else', function () {
expect(layoutRoot((string) $this->blade('<x-grid gap="space300" />'))['data-md-gap'])->toBe('space300')
->and(layoutRoot((string) $this->blade('<x-grid gap="1.5rem" />'))['data-md-gap'])->toBe('none');
});
it('fills a row with columns of a minimum width, capped by the counts when both are given', function () {
$fill = layoutRoot((string) $this->blade('<x-grid min-item="280px" />'));
$capped = layoutRoot((string) $this->blade('<x-grid min-item="18rem" :columns="[\'medium\' => 2, \'expanded\' => 3]" />'));
expect($fill)->toHaveKey('data-md-min-item')->not->toHaveKey('data-md-columns')
->and($fill['style'])->toContain('--md-min-item: 280px;')
->and($capped)->toHaveKeys(['data-md-min-item', 'data-md-columns'])
->and($capped['style'])->toContain('--md-min-item: 18rem;');
foreach (['240' => '240px', '20ch' => '20ch', '12.5em' => '12.5em'] as $given => $length) {
expect(layoutRoot((string) $this->blade('<x-grid :min-item="$m" />', ['m' => $given]))['style'])->toContain("--md-min-item: {$length};");
}
foreach (['wide', '50%', 'calc(10px + 1rem)', '0'] as $given) {
expect(layoutRoot((string) $this->blade('<x-grid :min-item="$m" />', ['m' => $given])))->not->toHaveKey('data-md-min-item')
->and((string) $this->blade('<x-grid :min-item="$m" />', ['m' => $given]))->not->toContain('--md-min-item');
}
});
it('takes the element, the visibility props, and the caller\'s class and style after its own', function () {
$root = layoutRoot((string) $this->blade('<x-grid as="ul" hide-below="expanded" class="gallery" style="align-items: start" />'));
expect($root)->toMatchArray(['<' => 'ul', 'data-md-hide-below' => 'expanded', 'class' => 'gallery'])
->and($root['style'])->toEndWith('--md-columns-extra-large: 1; align-items: start;');
});
it('reads its own column count at each breakpoint in the layout layer', function () {
$css = (string) file_get_contents(__DIR__.'/../../../resources/css/layout/grid.css');
expect($css)->toContain('@layer material.layout')
->toContain('grid-template-columns: repeat(var(--md-columns), minmax(0, 1fr));');
foreach (['medium' => 600, 'expanded' => 840, 'large' => 1200, 'extra-large' => 1600] as $breakpoint => $width) {
expect($css)->toContain("@media (width >= {$width}px) {\n --md-columns: var(--md-columns-{$breakpoint}, 1);");
}
expect((string) file_get_contents(__DIR__.'/../../../resources/css/all.css'))->toContain("@import './layout/grid.css';");
});