Files
livewire-material/tests/Feature/Components/BadgeTest.php
T
Andreas ReinholdandClaude Opus 5 56d82b71f3 Put the badge on M3's anchor geometry and a visible edge
A floating dot sat 2px inside the icon's top-trailing corner where M3 puts it
flush, and a floating count 4px above it where M3 says 2px. An `outline` status
label drew its edge in outline-variant, the decorative role dividers use, which
is about 1.5:1 on surface — M3 asks a badge for 3:1, so it takes `outline`.
Plan step 18, actions.md ACT-23, ACT-24.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
2026-09-14 06:11:52 +02:00

93 lines
5.7 KiB
PHP

<?php
it('is M3\'s small badge without a value', function () {
expect((string) $this->blade('<x-badge />'))
->toContain('size-1.5 rounded-corner-full')
->toContain('bg-error text-on-error')
->toContain('aria-hidden="true"');
});
it('is M3\'s large badge with a count, capped by max', function () {
expect((string) $this->blade('<x-badge value="4" />'))->toContain('h-4 min-w-4')->toContain('>4</span>')
->and((string) $this->blade('<x-badge value="1204" max="999" />'))->toContain('>999+</span>')
->and((string) $this->blade('<x-badge value="12" max="999" color="primary" />'))->toContain('>12</span>')->toContain('bg-primary text-on-primary');
});
it('pins itself to the corner of an icon when floating', function () {
expect((string) $this->blade('<x-badge floating />'))->toContain('absolute top-0 end-0')
->and((string) $this->blade('<x-badge value="3" floating label="3 new messages" />'))
->toContain('absolute -top-0.5')
->toContain('aria-label="3 new messages"')
->not->toContain('aria-hidden');
});
it('draws a status label in a container or an outline', function () {
expect((string) $this->blade('<x-badge value="Active" tone="success" tonal />'))
->toContain('h-6 gap-1 rounded-corner-sm px-2 type-label-md')
->toContain('bg-success-container text-on-success-container')
->not->toContain('aria-hidden')
->and((string) $this->blade('<x-badge value="Pro" outline />'))
->toContain('border border-outline text-on-surface-variant')
->not->toContain('bg-error');
});
it('renders its slot as HTML, and is a dot while the slot holds nothing but comments', function () {
$html = (string) $this->blade('<x-badge tonal><x-icon name="bolt" class="size-3" /> Pro</x-badge>');
expect($html)
->toContain('h-6 gap-1 rounded-corner-sm')
->toContain('<svg')
->toContain(' Pro</span>')
->not->toContain('&lt;svg')
->and((string) $this->blade('<x-badge value="<b>4</b>" />'))->toContain('&lt;b&gt;4&lt;/b&gt;')
->and((string) $this->blade("<x-badge tonal>\n <!-- nothing yet -->\n</x-badge>"))->toContain('size-1.5 rounded-corner-full')
->and((string) $this->blade('<x-badge max="99">120</x-badge>'))->toContain('>99+</span>');
});
it('draws neutral ink on every variant', function () {
expect((string) $this->blade('<x-badge color="neutral" />'))->toContain('size-1.5 rounded-corner-full bg-on-surface-variant text-surface')
->and((string) $this->blade('<x-badge value="7" color="neutral" />'))->toContain('tabular-nums bg-on-surface-variant text-surface')
->and((string) $this->blade('<x-badge value="Draft" tone="neutral" tonal />'))->toContain('type-label-md bg-surface-container-high text-on-surface-variant')
->and((string) $this->blade('<x-badge value="Draft" color="neutral" outline />'))->toContain('type-label-md border border-outline text-on-surface-variant');
});
it('leaves a plain badge to the caller\'s colour classes', function (string $badge, string $shape) {
$html = (string) $this->blade($badge);
preg_match('/class="([^"]*)"/', $html, $class);
expect($class[1])
->toContain($shape)
->toEndWith('bg-tertiary-container text-on-tertiary-container')
->and(preg_replace('/bg-tertiary-container text-on-tertiary-container$/', '', $class[1]))->not->toMatch('/(^|\s)(bg|text|border)-/');
})->with([
'dot' => ['<x-badge color="plain" class="bg-tertiary-container text-on-tertiary-container" />', 'size-1.5 rounded-corner-full'],
'count' => ['<x-badge value="3" color="plain" class="bg-tertiary-container text-on-tertiary-container" />', 'h-4 min-w-4 rounded-corner-full px-1 type-label-sm tabular-nums'],
'tonal' => ['<x-badge value="Run" color="plain" tonal class="bg-tertiary-container text-on-tertiary-container" />', 'h-6 gap-1 rounded-corner-sm px-2 type-label-md'],
'outline' => ['<x-badge value="Run" color="plain" outline class="bg-tertiary-container text-on-tertiary-container" />', 'h-6 gap-1 rounded-corner-sm px-2 type-label-md border'],
]);
it('keeps its default colours, and falls back to error on a colour it does not know', function () {
expect((string) $this->blade('<x-badge value="4" />'))
->toContain('class="inline-flex shrink-0 items-center justify-center whitespace-nowrap h-4 min-w-4 rounded-corner-full px-1 type-label-sm tabular-nums bg-error text-on-error"')
->and((string) $this->blade('<x-badge value="Active" tonal />'))
->toContain('class="inline-flex shrink-0 items-center justify-center whitespace-nowrap h-6 gap-1 rounded-corner-sm px-2 type-label-md bg-error-container text-on-error-container"')
->and((string) $this->blade('<x-badge value="Pro" outline color="success" />'))
->toContain('class="inline-flex shrink-0 items-center justify-center whitespace-nowrap h-6 gap-1 rounded-corner-sm px-2 type-label-md border border-outline text-on-surface-variant"')
->and((string) $this->blade('<x-badge value="3" color="sport-run" />'))->toContain('bg-error text-on-error');
});
it('draws a solid status label in the colour itself, spoken like any label', function () {
$html = (string) $this->blade('<x-badge value="Built in" solid color="primary" />');
expect($html)
->toContain('bg-primary text-on-primary')
->toContain('h-6 gap-1 rounded-corner-sm px-2 type-label-md')
->not->toContain('aria-hidden')
->toContain('>Built in<')
->and((string) $this->blade('<x-badge value="Draft" solid color="neutral" />'))
->toContain('bg-on-surface-variant text-surface')
->and((string) $this->blade('<x-badge solid />'))
->toContain('size-1.5');
});