Add buttons, menus and the rest of M3 Expressive's actions
tests / browser (firefox, firefox) (push) Successful in 1m54s
tests / browser (safari, webkit) (push) Successful in 2m17s
tests / lint (push) Successful in 59s
tests / feature (8.4) (push) Successful in 1m7s
tests / feature (8.5) (push) Successful in 1m0s
tests / browser (chrome, chromium) (push) Successful in 1m49s
tests / browser (firefox, firefox) (push) Successful in 1m54s
tests / browser (safari, webkit) (push) Successful in 2m17s
tests / lint (push) Successful in 59s
tests / feature (8.4) (push) Successful in 1m7s
tests / feature (8.5) (push) Successful in 1m0s
tests / browser (chrome, chromium) (push) Successful in 1m49s
<x-button> (label buttons, icon buttons and toggles in five sizes, with filled, tonal, outlined, elevated and text variants in any colour role), <x-tooltip>, <x-menu> with items, groups and separators, <x-button-group>, <x-group> as a connected button group, <x-split-button>, <x-fab>, <x-fab-menu> and <x-loading>. Sizes, colours and shapes come from androidx Compose Material 3's tokens; the loading indicator ports its Morph into SVG + SMIL. Menus follow WAI-ARIA's menu button pattern on popovers placed by CSS anchor positioning. Browser tests run in Chromium, Firefox and WebKit. The showcase fetches the icon names on demand: inlined, they tripped Pest's test server into HTTP 431s under Firefox. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01V9NnLxnPp8vaaurb3Z1MFy
This commit is contained in:
co-authored by
Claude Opus 5
parent
b48e879254
commit
cd64f4f371
@@ -0,0 +1,85 @@
|
||||
<?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')
|
||||
->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-icon-button')
|
||||
->and((string) $this->blade('<x-button label="Bold" />'))->not->toContain('data-icon-button');
|
||||
});
|
||||
|
||||
it('draws a choice as connected radios', function () {
|
||||
$html = (string) $this->blade(<<<'BLADE'
|
||||
<x-group label="Theme" name="theme" x-model="theme" :options="[
|
||||
['id' => 'light', 'name' => 'Light', 'icon' => 'light_mode'],
|
||||
['id' => 'dark', 'name' => 'Dark', 'disabled' => true],
|
||||
]" />
|
||||
BLADE);
|
||||
|
||||
expect($html)
|
||||
->toContain('<legend class="mb-2 type-label-lg text-on-surface-variant">Theme</legend>')
|
||||
->toContain('data-button-group="connected"')
|
||||
->toContain('type="radio"')
|
||||
->toContain('name="theme"')
|
||||
->toContain('x-model="theme"')
|
||||
->toContain('value="light"')
|
||||
->toContain('has-checked:bg-secondary has-checked:text-on-secondary')
|
||||
->toContain('disabled')
|
||||
->and(substr_count($html, '<svg'))->toBe(1);
|
||||
});
|
||||
|
||||
it('draws several choices as checkboxes', function () {
|
||||
expect((string) $this->blade('<x-group name="days" multiple variant="outlined" :options="[[\'id\' => \'mon\', \'name\' => \'Mon\']]" />'))
|
||||
->toContain('type="checkbox"')
|
||||
->toContain('name="days[]"')
|
||||
->toContain('has-checked:bg-inverse-surface');
|
||||
});
|
||||
|
||||
it('binds to a Livewire property and shows its validation message', function () {
|
||||
$component = new class extends Component
|
||||
{
|
||||
public string $theme = 'dark';
|
||||
|
||||
public function save(): void
|
||||
{
|
||||
$this->validate(['theme' => 'in:light'], ['theme.in' => 'Pick light.']);
|
||||
}
|
||||
|
||||
public function render(): string
|
||||
{
|
||||
return <<<'BLADE'
|
||||
<div>
|
||||
<x-group wire:model="theme" hint="How it looks" :options="[['id' => 'light', 'name' => 'Light'], ['id' => 'dark', 'name' => 'Dark']]" />
|
||||
</div>
|
||||
BLADE;
|
||||
}
|
||||
};
|
||||
|
||||
Livewire::test($component)
|
||||
->assertSeeHtml('wire:model="theme"')
|
||||
->assertSee('How it looks')
|
||||
->set('theme', 'light')
|
||||
->assertSet('theme', 'light')
|
||||
->set('theme', 'dark')
|
||||
->call('save')
|
||||
->assertSee('Pick light.')
|
||||
->assertDontSee('How it looks');
|
||||
});
|
||||
Reference in New Issue
Block a user