Plan step 36. <x-group> renders data-md-group with its size, variant, shape, multiple and inline, a data-md-button-group row and data-md-group-segment labels over its radios or checkboxes. group.css draws the segments as M3's buttons with the toggle button's colours from :has(:checked), the disabled treatment from :has(:disabled), the focus ring from :has(:focus-visible) and the 48px target at xs and sm. The connected shapes move into button-group.css (the corner scale, square ends, the pressed and selected inner corners), which group.css imports; <x-button-group> adopts them in its own rewrite. The group's render tests move to GroupTest; AppBarTest (navigation group) follows the new legend and row hooks of theme-toggle's rows. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
91 lines
4.1 KiB
PHP
91 lines
4.1 KiB
PHP
<?php
|
|
|
|
use Livewire\Component;
|
|
use Livewire\Livewire;
|
|
|
|
it('groups buttons with the spacing of their size', function () {
|
|
expect((string) $this->blade('<x-button-group label="Views" size="md"><x-button label="Day" /></x-button-group>'))
|
|
->toContain('role="group"')
|
|
->toContain('aria-label="Views"')
|
|
->toContain('data-button-group="standard"')
|
|
->toContain('data-size="md"')
|
|
->toContain('gap-2')
|
|
->and((string) $this->blade('<x-button-group><x-button label="Day" /></x-button-group>'))
|
|
->toContain('gap-3');
|
|
});
|
|
|
|
it('connects buttons 2px apart', function () {
|
|
expect((string) $this->blade('<x-button-group connected><x-button label="Day" /></x-button-group>'))
|
|
->toContain('data-button-group="connected"')
|
|
->toContain('gap-0.5');
|
|
});
|
|
|
|
it('squares a group', function () {
|
|
// M3 lists "Default shape | Round, square" as a button-group configuration; the shape covers
|
|
// every button in the group, so it need not be written on each one.
|
|
expect((string) $this->blade('<x-button-group shape="square"><x-button label="Day" /></x-button-group>'))
|
|
->toContain('data-shape="square"')
|
|
->and((string) $this->blade('<x-button-group connected shape="square"><x-button label="Day" /></x-button-group>'))
|
|
->toContain('data-button-group="connected"')
|
|
->toContain('data-shape="square"')
|
|
->and((string) $this->blade('<x-button-group><x-button label="Day" /></x-button-group>'))
|
|
->toContain('data-shape="round"');
|
|
});
|
|
|
|
it('takes over aria-pressed for a group that carries a selection', function () {
|
|
$html = (string) $this->blade('<x-button-group connected selection="single" required label="View"><x-button label="Day" value="day" :selected="true" /></x-button-group>');
|
|
|
|
expect($html)
|
|
->toContain('data-selection="single"')
|
|
->toContain('data-selection-required')
|
|
->toContain('multiple: false')
|
|
->toContain('required: true')
|
|
->toContain('value: null')
|
|
->toContain('x-on:click="press($event)"')
|
|
// No model: the group takes the state from the buttons' own aria-pressed and goes on.
|
|
->toContain('x-modelable="value"')
|
|
->and((string) $this->blade('<x-button-group connected selection="multi"><x-button label="Bold" value="bold" :selected="false" /></x-button-group>'))
|
|
->toContain('multiple: true')
|
|
->toContain('required: false')
|
|
->toContain('value: []')
|
|
->and((string) $this->blade('<x-button-group connected><x-button label="Day" /></x-button-group>'))
|
|
->not->toContain('data-selection')
|
|
->not->toContain('press($event)');
|
|
});
|
|
|
|
it('entangles a selection group with the property it binds, and keeps wire:model off the div', function () {
|
|
$component = new class extends Component
|
|
{
|
|
public string $view = 'week';
|
|
|
|
public function render(): string
|
|
{
|
|
return <<<'BLADE'
|
|
<div>
|
|
<x-button-group connected selection="single" required wire:model.live="view" label="View">
|
|
<x-button label="Day" value="day" :selected="$view === 'day'" />
|
|
<x-button label="Week" value="week" :selected="$view === 'week'" />
|
|
</x-button-group>
|
|
</div>
|
|
BLADE;
|
|
}
|
|
};
|
|
|
|
$html = Livewire::test($component)->assertSet('view', 'week')->html();
|
|
|
|
expect($html)
|
|
->toContain('data-selection="single"')
|
|
->toMatch('/value: window\.Livewire\.find\(/')
|
|
->and(substr_count($html, 'wire:model.live="view"'))->toBe(0);
|
|
});
|
|
|
|
it('never wraps a group onto a second line', function () {
|
|
expect((string) $this->blade('<x-button-group><x-button label="Day" /></x-button-group>'))
|
|
->not->toContain('flex-wrap');
|
|
});
|
|
|
|
it('marks icon buttons, which keep their width when a group is pressed', function () {
|
|
expect((string) $this->blade('<x-button icon="format_bold" aria-label="Bold" />'))->toContain('data-md-icon-button')
|
|
->and((string) $this->blade('<x-button label="Bold" />'))->not->toContain('data-md-icon-button');
|
|
});
|