104 lines
4.0 KiB
PHP
104 lines
4.0 KiB
PHP
<?php
|
|
|
|
use Illuminate\Support\Facades\Blade;
|
|
|
|
/**
|
|
* An icon sits BESIDE its label, at the size the call site asked for.
|
|
*
|
|
* Both halves of that sentence were broken at once in the console sidebar: the
|
|
* "Zugangsdaten" entry rendered the lock above the word instead of next to it,
|
|
* and at 20px instead of the 16px it was written as. The two causes are
|
|
* independent, so they are tested independently.
|
|
*/
|
|
|
|
it('renders an icon at the size the call site asked for', function () {
|
|
// Not a style preference: `.size-4` and `.size-5` have the same
|
|
// specificity, so the one Tailwind emits LATER wins — and it emits size-4
|
|
// first. A component that always merges its own size therefore overrides
|
|
// every caller that picked a smaller one.
|
|
$html = Blade::render('<x-ui.icon name="lock" class="size-4" />');
|
|
|
|
expect($html)->toContain('size-4')->not->toContain('size-5');
|
|
});
|
|
|
|
it('falls back to its own size when the call site names none', function () {
|
|
expect(Blade::render('<x-ui.icon name="lock" />'))->toContain('size-5');
|
|
});
|
|
|
|
it('keeps an icon on the line it was written on', function () {
|
|
// Tailwind's preflight makes every svg display:block. Inside an inline
|
|
// parent that pushes the following text onto a second line, which is how a
|
|
// one-line navigation entry became two lines tall.
|
|
expect(Blade::render('<x-ui.icon name="lock" />'))
|
|
->toContain('inline-block')
|
|
->toContain('shrink-0');
|
|
});
|
|
|
|
it('keeps a navigation entry on one line whichever slot the icon uses', function () {
|
|
// Blade::render leaves a slot's output buffer open when a component is
|
|
// rendered outside a request, which PHPUnit reports as a risky test. The
|
|
// markup it returns is complete; only the buffer needs tidying.
|
|
$render = function (string $template): string {
|
|
$level = ob_get_level();
|
|
|
|
try {
|
|
return Blade::render($template);
|
|
} finally {
|
|
while (ob_get_level() > $level) {
|
|
ob_end_clean();
|
|
}
|
|
}
|
|
};
|
|
|
|
$viaSlot = $render(
|
|
'<x-ui.nav-item href="/x"><x-slot:icon><x-ui.icon name="lock" /></x-slot:icon>Zugangsdaten</x-ui.nav-item>'
|
|
);
|
|
|
|
// The mistake that caused the report: the icon handed to the DEFAULT slot
|
|
// rather than the icon slot. The layout must not depend on which was used.
|
|
$viaDefaultSlot = $render(
|
|
'<x-ui.nav-item href="/x"><x-ui.icon name="lock" />Zugangsdaten</x-ui.nav-item>'
|
|
);
|
|
|
|
foreach ([$viaSlot, $viaDefaultSlot] as $html) {
|
|
expect($html)
|
|
->toContain('items-center')
|
|
->toContain('Zugangsdaten')
|
|
// The label wrapper is a flex row of its own, so a stray icon lands
|
|
// next to the text rather than above it.
|
|
->toContain('flex min-w-0 items-center gap-3');
|
|
}
|
|
});
|
|
|
|
it('puts navigation icons in the icon slot everywhere', function () {
|
|
// Belt to the component's braces. The component now survives an icon in the
|
|
// wrong slot, but the wrong slot also skips the shrink-0 wrapper and reads
|
|
// as an accident to the next person editing the file.
|
|
$offenders = [];
|
|
|
|
$files = new RecursiveIteratorIterator(
|
|
new RecursiveDirectoryIterator(resource_path('views'), FilesystemIterator::SKIP_DOTS)
|
|
);
|
|
|
|
foreach ($files as $file) {
|
|
if (! str_ends_with($file->getFilename(), '.blade.php')) {
|
|
continue;
|
|
}
|
|
|
|
$source = (string) file_get_contents($file->getPathname());
|
|
|
|
// Each nav-item element, from its opening tag to its closing one.
|
|
preg_match_all('/<x-ui\.nav-item\b.*?<\/x-ui\.nav-item>/s', $source, $matches);
|
|
|
|
foreach ($matches[0] as $element) {
|
|
$withoutIconSlot = preg_replace('/<x-slot:icon>.*?<\/x-slot:icon>/s', '', $element);
|
|
|
|
if (str_contains((string) $withoutIconSlot, '<x-ui.icon')) {
|
|
$offenders[] = str_replace(resource_path('views').'/', '', $file->getPathname());
|
|
}
|
|
}
|
|
}
|
|
|
|
expect($offenders)->toBe([]);
|
|
});
|