The slot was cast to a string and escaped, so an icon beside a badge's word showed as literal markup. It renders as HTML now; `value` stays escaped, and a slot holding only whitespace or comments is still a dot (hasActualContent rather than isNotEmpty). `color="neutral"` draws neutral ink on every variant: a dot or count in on-surface-variant with surface text (the ink the outline badge already writes in), a tonal label in surface-container-high with on-surface-variant text, and an outline in outline-variant. `color="plain"` emits no background, text or border colour in any variant, keeping shape, size and type, so an application's own colour classes paint it without racing the badge's. Every existing colour renders the same classes as before, and an unknown colour still falls back to error. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RHoXZSHc8gGpZjFmA5fPc2
79 lines
5.1 KiB
PHP
79 lines
5.1 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.5 end-0.5')
|
|
->and((string) $this->blade('<x-badge value="3" floating label="3 new messages" />'))
|
|
->toContain('absolute -top-1')
|
|
->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-variant 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('<svg')
|
|
->and((string) $this->blade('<x-badge value="<b>4</b>" />'))->toContain('<b>4</b>')
|
|
->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-variant 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-variant text-on-surface-variant"')
|
|
->and((string) $this->blade('<x-badge value="3" color="sport-run" />'))->toContain('bg-error text-on-error');
|
|
});
|