Add hint-class to the group and icon-class to menu items
<x-group hint-class> adds classes to the hint, as the text fields' hint-class does, and a validation message still replaces the hint. <x-menu-item icon-class> adds classes to the leading icon, for an icon whose colour means something of its own, such as a sport's glyph. A colour class there has to win over the component's own colour, and which of two colour utilities wins depends on the order Tailwind emits them (text-error comes before text-on-surface-variant). So when either prop is given, the component's own colour is written with a :where() variant that carries no specificity, as the fields' hint colour sits in the components layer. A disabled item's icon stays disabled. Without the props the markup is unchanged. The skill now also lists the fields' hint-class, which it had left out. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RHoXZSHc8gGpZjFmA5fPc2
This commit is contained in:
co-authored by
Claude Opus 5
parent
a83d7f62ea
commit
ab7317faae
@@ -221,3 +221,33 @@ it('hangs a menu on its menu button, even when the button is fixed to a corner o
|
||||
->assertScript("(() => { const names = getComputedStyle(document.querySelector('[data-test=\"more\"]')).getPropertyValue('anchor-name'); return names.includes('--material-button-') && names.includes('--material-menu-'); })()")
|
||||
->assertScript('(({ control, menu }) => menu.top >= control.bottom && menu.top - control.bottom <= 16 && Math.abs(menu.left - control.left) <= 16)('.menuAgainst('more', 'Share actions').')');
|
||||
});
|
||||
|
||||
it('paints a group\'s hint and a menu item\'s icon in the colour their classes name', function () {
|
||||
Route::middleware('web')->get('/colour-class-probe', fn () => Blade::render(<<<'BLADE'
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<x-theme-script />
|
||||
@vite(config('livewire-material.showcase.vite'))
|
||||
</head>
|
||||
<body class="bg-surface">
|
||||
<span id="error-ink" class="text-error">Reference</span>
|
||||
<div id="terrain">
|
||||
<x-group name="terrain" hint="No elevation data here" hint-class="text-error" :options="[['id' => 'flat', 'name' => 'Flat']]" />
|
||||
</div>
|
||||
<x-menu-item id="run" label="Running plan" icon="directions_run" icon-class="text-error" />
|
||||
<x-menu-item id="chosen" label="Cycling plan" icon="directions_bike" icon-class="text-error" :selected="true" />
|
||||
<x-menu-item id="off" label="Swimming plan" icon="pool" icon-class="text-error" disabled />
|
||||
</body>
|
||||
</html>
|
||||
BLADE));
|
||||
|
||||
$ink = fn (string $element): string => "getComputedStyle({$element}).color";
|
||||
$error = $ink("document.querySelector('#error-ink')");
|
||||
|
||||
visit('/colour-class-probe')->waitForEvent('networkidle')
|
||||
->assertScript($ink("document.querySelector('#terrain p')")." === {$error}")
|
||||
->assertScript($ink("document.querySelector('#run svg')")." === {$error}")
|
||||
->assertScript($ink("document.querySelector('#chosen svg')")." === {$error}")
|
||||
->assertScript($ink("document.querySelector('#off svg')")." !== {$error}");
|
||||
});
|
||||
|
||||
@@ -83,3 +83,17 @@ it('binds to a Livewire property and shows its validation message', function ()
|
||||
->assertSee('Pick light.')
|
||||
->assertDontSee('How it looks');
|
||||
});
|
||||
|
||||
it('adds hint-class to the hint, which a validation message still replaces', function () {
|
||||
$options = [['id' => 'flat', 'name' => 'Flat'], ['id' => 'hilly', 'name' => 'Hilly']];
|
||||
|
||||
expect((string) $this->blade('<x-group wire:model="terrain" hint="No elevation data here" hint-class="text-warning" :$options />', ['options' => $options]))
|
||||
->toContain('<p class="mt-1 type-body-sm [:where(&)]:text-on-surface-variant text-warning">No elevation data here</p>')
|
||||
->and((string) $this->blade('<x-group wire:model="terrain" hint="No elevation data here" :$options />', ['options' => $options]))
|
||||
->toContain('<p class="mt-1 type-body-sm text-on-surface-variant">No elevation data here</p>');
|
||||
|
||||
expect((string) $this->withViewErrors(['terrain' => 'Pick a terrain.'])->blade('<x-group wire:model="terrain" hint="No elevation data here" hint-class="text-warning" :$options />', ['options' => $options]))
|
||||
->toContain('<p class="mt-1 type-body-sm text-error">Pick a terrain.</p>')
|
||||
->not->toContain('No elevation data here')
|
||||
->not->toContain('text-warning');
|
||||
});
|
||||
|
||||
@@ -69,3 +69,18 @@ it('separates and labels groups', function () {
|
||||
$this->blade('<x-menu-group label="Sort by"><x-menu-item label="Newest" /></x-menu-group>')
|
||||
->assertSee('role="group" aria-label="Sort by"', false);
|
||||
});
|
||||
|
||||
it('adds icon-class to the leading icon, over its own colour but not over disabled', function () {
|
||||
$leading = fn (string $html): string => preg_match('/<svg[^>]*class="([^"]*)"/', $html, $icon) ? $icon[1] : '';
|
||||
|
||||
expect($leading((string) $this->blade('<x-menu-item label="Running plan" icon="directions_run" icon-class="text-sport-run" icon-right="chevron_right" />')))
|
||||
->toBe('shrink-0 size-5 [:where(&)]:text-on-surface-variant text-sport-run')
|
||||
->and($leading((string) $this->blade('<x-menu-item label="Running plan" icon="directions_run" icon-class="text-sport-run" :selected="true" />')))
|
||||
->toBe('shrink-0 size-5 [:where(&)]:text-on-tertiary-container text-sport-run')
|
||||
->and($leading((string) $this->blade('<x-menu-item label="Running plan" icon="directions_run" icon-class="text-sport-run" disabled />')))
|
||||
->toBe('shrink-0 size-5 text-sport-run text-on-surface/38!')
|
||||
->and($leading((string) $this->blade('<x-menu-item label="Running plan" icon="directions_run" />')))
|
||||
->toBe('shrink-0 size-5 text-on-surface-variant')
|
||||
->and((string) $this->blade('<x-menu-item label="Next" icon-right="chevron_right" icon-class="text-sport-run" />'))
|
||||
->not->toContain('text-sport-run');
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user