Plan step 36 ("Interaction is the shared classes", the user, 2026-09-14).
button.css hand-rolled the same declarations as foundation/interaction.css's
md-state-layer, md-focus-ring and md-touch-target; it now renders those
classes (the touch target only at xs and sm, where it draws under 48px) and
keeps only its own geometry, colour and motion. The stylesheet test's "writes
no class list" check now allows the three interaction classes, in any subset,
and $attributes->class() as the way a root merges them with a caller's own
class; split-button and button-group, which nest a button, are updated to match.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
69 lines
4.5 KiB
PHP
69 lines
4.5 KiB
PHP
<?php
|
|
|
|
use NoNameWeb\LivewireMaterial\Tests\Support\ComponentStylesheet;
|
|
|
|
it('puts the action on the leading button and the menu on the trailing one', function () {
|
|
$html = (string) $this->blade(<<<'BLADE'
|
|
<x-split-button label="Download all" icon="download" wire:click="downloadZip" menu-label="Download options" class="mt-4">
|
|
<x-menu-item label="As a ZIP" />
|
|
</x-split-button>
|
|
BLADE);
|
|
|
|
expect($html)
|
|
->toStartWith('<div data-md-split-button data-md-size="sm" class="mt-4">')
|
|
->toMatch('/<button class="md-state-layer md-focus-ring md-touch-target" data-md-button="data-md-button"[^>]*data-md-split="leading" wire:click="downloadZip">/')
|
|
->toMatch('/<button class="md-state-layer md-focus-ring md-touch-target" data-md-button="data-md-button"[^>]*data-md-icon-button="data-md-icon-button"[^>]*aria-label="Download options"[^>]*data-md-split="trailing">/')
|
|
->toContain('role="menu"')
|
|
->toContain('As a ZIP')
|
|
->and(substr_count($html, 'wire:click="downloadZip"'))->toBe(1);
|
|
});
|
|
|
|
it('leaves the corners to the pair, growing the inner ones under the finger', function () {
|
|
$css = ComponentStylesheet::read('split-button');
|
|
|
|
expect($css->declarations('[data-md-split-button] [data-md-split]'))->toBe([
|
|
'--md-split-button-corner' => 'var(--md-split-button-inner)',
|
|
'border-start-start-radius' => 'var(--md-split-button-corner)',
|
|
'border-end-start-radius' => 'var(--md-split-button-corner)',
|
|
'border-start-end-radius' => 'var(--md-split-button-corner)',
|
|
'border-end-end-radius' => 'var(--md-split-button-corner)',
|
|
])
|
|
->and($css->declarations('[data-md-split-button] [data-md-split]:active'))->toBe(['--md-split-button-corner' => 'var(--md-split-button-inner-pressed)'])
|
|
->and($css->declarations("[data-md-split-button] [data-md-split='trailing'][aria-expanded='true']"))->toBe(['--md-split-button-corner' => 'var(--md-split-button-full)'])
|
|
->and($css->declarations("[data-md-split-button][data-md-size='xl']"))->toBe([
|
|
'--md-split-button-inner' => 'var(--md-sys-shape-corner-md)',
|
|
'--md-split-button-inner-pressed' => 'var(--md-sys-shape-corner-lg-increased)',
|
|
'--md-split-button-full' => '68px',
|
|
'--md-split-button-icon' => '50px',
|
|
'--md-split-button-nudge' => '6px',
|
|
])
|
|
->and($css->imports())->toBe(['./button.css', './menu.css']);
|
|
});
|
|
|
|
it('is filled unless another container variant is asked for', function () {
|
|
expect((string) $this->blade('<x-split-button label="Save" variant="text"><x-menu-item label="Draft" /></x-split-button>'))->toContain('data-md-variant="filled"')->not->toContain('data-md-variant="text"')
|
|
->and((string) $this->blade('<x-split-button label="Save" variant="tonal"><x-menu-item label="Draft" /></x-split-button>'))->toContain('data-md-variant="tonal"');
|
|
});
|
|
|
|
it('gives the two smallest sizes their narrower inner padding and 48px trailing button', function () {
|
|
$css = ComponentStylesheet::read('split-button');
|
|
|
|
expect((string) $this->blade('<x-split-button label="Save" size="xs"><x-menu-item label="Draft" /></x-split-button>'))->toContain('data-md-split-button data-md-size="xs"')
|
|
->and($css->declarations("[data-md-split-button][data-md-size='xs'] [data-md-split='leading']"))->toBe(['padding-inline-end' => 'var(--md-sys-measurement-space125)'])
|
|
->and($css->declarations("[data-md-split-button][data-md-size='sm'] [data-md-split='leading']"))->toBe(['padding-inline-end' => '12px'])
|
|
->and($css->declarations("[data-md-split-button]:is([data-md-size='xs'], [data-md-size='sm']) [data-md-split='trailing']"))->toBe(['inline-size' => 'var(--md-sys-measurement-space600)']);
|
|
});
|
|
|
|
it('nudges its chevron towards the leading half and turns it over on the standard scheme', function () {
|
|
$css = ComponentStylesheet::read('split-button');
|
|
|
|
expect($css->declarations("[data-md-split-button] [data-md-split='trailing'] [data-md-icon]"))->toBe([
|
|
'inline-size' => 'var(--md-split-button-icon)',
|
|
'block-size' => 'var(--md-split-button-icon)',
|
|
'margin-inline-start' => 'calc(-1 * var(--md-split-button-nudge))',
|
|
'margin-inline-end' => 'var(--md-split-button-nudge)',
|
|
'transition' => 'rotate var(--md-sys-motion-duration-short) var(--md-sys-motion-easing-standard)',
|
|
])
|
|
->and($css->declarations("[data-md-split-button] [data-md-split='trailing'][aria-expanded='true'] [data-md-icon]"))->toBe(['rotate' => '180deg']);
|
|
});
|