Draw the button group without Tailwind
Plan step 36. <x-button-group> renders data-md-button-group (standard or connected), data-md-size, data-md-shape and, with a selection, data-md-selection and data-md-selection-required. button-group.css now also draws the standard group: its gaps per size, the square corner scale it hands its buttons, and the press expansion that widens a pressed label button while its neighbours give way. groups.css keeps only the split button until that is rewritten too. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
This commit is contained in:
co-authored by
Claude Opus 5
parent
b8c6d411db
commit
8dd0a7ef9d
@@ -1,7 +1,17 @@
|
||||
/*
|
||||
* <x-button-group>'s shapes: the corners a button takes from its place among its siblings. The
|
||||
* connected form is also what `<x-group>` draws its segments with, which is why its stylesheet
|
||||
* imports this one.
|
||||
* <x-button-group>: related buttons in a row, and the shapes a button takes from its place among
|
||||
* its siblings. The connected form is also what `<x-group>` draws its segments with, which is why
|
||||
* its stylesheet imports this one. A group never wraps: M3's rule, and the press expansion only
|
||||
* reaches a neighbour on the same line anyway.
|
||||
*
|
||||
* Standard (`data-md-button-group="standard"`): the buttons stand apart — 18/12/8/8/8px by size
|
||||
* (ButtonGroupXSmall…XLargeTokens.BetweenSpace) — and pressing a label button widens it and
|
||||
* narrows its neighbours by the same amount, so the row keeps its length: Expressive's press
|
||||
* expansion (ButtonGroupDefaults.ExpandedRatio is 15%; this uses a fixed step per size, 4/6/8/16/
|
||||
* 20px, on the button's own padding of 16/16/24/48/64px). Icon buttons keep their width. A square
|
||||
* standard group has no inner edges, so it hands its buttons the square corner scale
|
||||
* `<x-button shape="square">` draws (12/12/16/28/28px, 8/8/12/16/16px under the finger), and a
|
||||
* selected button in it turns round.
|
||||
*
|
||||
* Connected (`data-md-button-group="connected"`): 2px apart, the outer corners full, the inner
|
||||
* corners small, smaller still while pressed, and a selected segment (`aria-pressed`, or a checked
|
||||
@@ -34,29 +44,87 @@
|
||||
--md-button-group-inner: var(--md-sys-shape-corner-sm);
|
||||
--md-button-group-inner-pressed: var(--md-sys-shape-corner-xs);
|
||||
--md-button-group-full: 20px;
|
||||
--md-button-group-square: var(--md-sys-shape-corner-md);
|
||||
--md-button-group-square-pressed: var(--md-sys-shape-corner-sm);
|
||||
--md-button-group-outer: var(--md-button-group-full);
|
||||
--md-button-group-pad: var(--md-sys-measurement-space200);
|
||||
--md-button-group-grow: 6px;
|
||||
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
[data-md-button-group][data-md-size='xs'] {
|
||||
--md-button-group-inner: var(--md-sys-shape-corner-xs);
|
||||
--md-button-group-inner-pressed: 2px;
|
||||
--md-button-group-full: 16px;
|
||||
--md-button-group-grow: 4px;
|
||||
}
|
||||
|
||||
[data-md-button-group][data-md-size='md'] {
|
||||
--md-button-group-full: 28px;
|
||||
--md-button-group-square: var(--md-sys-shape-corner-lg);
|
||||
--md-button-group-square-pressed: var(--md-sys-shape-corner-md);
|
||||
--md-button-group-pad: var(--md-sys-measurement-space300);
|
||||
--md-button-group-grow: 8px;
|
||||
}
|
||||
|
||||
[data-md-button-group][data-md-size='lg'] {
|
||||
--md-button-group-inner: var(--md-sys-shape-corner-lg);
|
||||
--md-button-group-inner-pressed: var(--md-sys-shape-corner-md);
|
||||
--md-button-group-full: 48px;
|
||||
--md-button-group-square: var(--md-sys-shape-corner-xl);
|
||||
--md-button-group-square-pressed: var(--md-sys-shape-corner-lg);
|
||||
--md-button-group-pad: var(--md-sys-measurement-space600);
|
||||
--md-button-group-grow: 16px;
|
||||
}
|
||||
|
||||
[data-md-button-group][data-md-size='xl'] {
|
||||
--md-button-group-inner: var(--md-sys-shape-corner-lg-increased);
|
||||
--md-button-group-inner-pressed: var(--md-sys-shape-corner-lg);
|
||||
--md-button-group-full: 68px;
|
||||
--md-button-group-square: var(--md-sys-shape-corner-xl);
|
||||
--md-button-group-square-pressed: var(--md-sys-shape-corner-lg);
|
||||
--md-button-group-pad: var(--md-sys-measurement-space800);
|
||||
--md-button-group-grow: 20px;
|
||||
}
|
||||
|
||||
[data-md-button-group='standard'] {
|
||||
gap: var(--md-sys-measurement-space100);
|
||||
}
|
||||
|
||||
[data-md-button-group='standard'][data-md-size='xs'] {
|
||||
gap: 18px;
|
||||
}
|
||||
|
||||
[data-md-button-group='standard'][data-md-size='sm'] {
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
/* A square standard group hands its buttons the square corner scale; a selected one rounds. */
|
||||
[data-md-button-group='standard'][data-md-shape='square'] > * {
|
||||
border-radius: var(--md-button-group-square);
|
||||
}
|
||||
|
||||
[data-md-button-group='standard'][data-md-shape='square'] > :active:not(:disabled, [aria-disabled='true']) {
|
||||
border-radius: var(--md-button-group-square-pressed);
|
||||
}
|
||||
|
||||
[data-md-button-group='standard'][data-md-shape='square'] > [aria-pressed='true'] {
|
||||
border-radius: var(--md-button-group-full);
|
||||
}
|
||||
|
||||
/* Press expansion: the pressed label button grows, the label buttons beside it give way. */
|
||||
[data-md-button-group='standard'] > :not([data-md-icon-button]):active:not(:disabled, [aria-disabled='true']) {
|
||||
padding-inline: calc(var(--md-button-group-pad) + var(--md-button-group-grow));
|
||||
}
|
||||
|
||||
[data-md-button-group='standard'] > :not([data-md-icon-button]):has(+ :not([data-md-icon-button]):active:not(:disabled, [aria-disabled='true'])) {
|
||||
padding-inline-end: calc(var(--md-button-group-pad) - var(--md-button-group-grow));
|
||||
}
|
||||
|
||||
[data-md-button-group='standard'] > :not([data-md-icon-button]):active:not(:disabled, [aria-disabled='true']) + :not([data-md-icon-button]) {
|
||||
padding-inline-start: calc(var(--md-button-group-pad) - var(--md-button-group-grow));
|
||||
}
|
||||
|
||||
/* A square group's ends take the corner its inner edges take. */
|
||||
|
||||
@@ -1,87 +1,32 @@
|
||||
/*
|
||||
* Button groups and split buttons: shapes that depend on a button's place among its siblings.
|
||||
* Split buttons: shapes that depend on a button's place beside its other half. Button groups and
|
||||
* `<x-group>` draw theirs from resources/css/components/button-group.css.
|
||||
*
|
||||
* Unlayered on purpose. Each <x-button> draws its own corners and padding as utilities, and a
|
||||
* rule in any @layer loses to a utility whatever its specificity; the group has to win.
|
||||
* Unlayered on purpose, until the split button leaves Tailwind too.
|
||||
*
|
||||
* Standard group (`<x-button-group>`): pressing a label button widens it and narrows its
|
||||
* neighbours by the same amount, so the row keeps its length — Expressive's press expansion
|
||||
* (ButtonGroupDefaults.ExpandedRatio is 15%; this uses a fixed step per size). Icon buttons keep
|
||||
* their width.
|
||||
*
|
||||
* Connected group (`<x-button-group connected>`, `<x-group>`): 2px apart, the outer corners
|
||||
* full, the inner corners small, smaller still while pressed, and a selected segment fully round
|
||||
* (ConnectedButtonGroupSmallTokens and the M3 Expressive connected button group spec).
|
||||
*
|
||||
* Shape (`shape="square"`, M3's "Default shape | Round, square" configuration): `--group-outer`
|
||||
* is what the two ends of a group round to — half the height for a round group, and for a square
|
||||
* one the same corner its inner edges take, which is the square table M3 publishes for connected
|
||||
* groups (4/8/8/16/20dp by size). A square *standard* group has no inner edges, so it hands its
|
||||
* buttons the square corner scale `<x-button shape="square">` draws (`--group-square`, and
|
||||
* `--group-square-pressed` under the finger) instead of asking the caller to write it on each
|
||||
* one. Selection morphs the other way in both: M3 has a toggle inside a group "swap shape
|
||||
* square/round on selection", so a selected button in a square group turns round, which is why
|
||||
* the selected rules keep `--group-full` rather than `--group-outer`.
|
||||
*
|
||||
* Split button (`<x-split-button>`): the same idea for two halves, on its own corner scale —
|
||||
* SplitButton*Tokens gives the inner corner 4/4/4/8/12px at rest and *grows* it to 8/12/12/20/20px
|
||||
* under the finger, the opposite of a connected group, so `--split-inner*` is separate from
|
||||
* `--group-inner*`. The trailing half turns round and its chevron turns over while its menu is
|
||||
* open, on M3's standard scheme rather than the expressive springs (the site: "the trailing
|
||||
* button uses the standard motion scheme, not expressive, when rotating"), and the chevron sits
|
||||
* a little towards the leading half so the pair reads as one control (`--split-nudge`:
|
||||
* 1/1/2/3/6px). `--split-icon` is the trailing icon's own size, 22/22/26/38/50px, which the
|
||||
* icon-button table does not know about.
|
||||
* Split button (`<x-split-button>`): two halves on their own corner scale — SplitButton*Tokens
|
||||
* gives the inner corner 4/4/4/8/12px at rest and *grows* it to 8/12/12/20/20px under the finger,
|
||||
* the opposite of a connected group. The trailing half turns round and its chevron turns over
|
||||
* while its menu is open, on M3's standard scheme rather than the expressive springs (the site:
|
||||
* "the trailing button uses the standard motion scheme, not expressive, when rotating"), and the
|
||||
* chevron sits a little towards the leading half so the pair reads as one control
|
||||
* (`--split-nudge`: 1/1/2/3/6px). `--split-icon` is the trailing icon's own size,
|
||||
* 22/22/26/38/50px, which the icon-button table does not know about.
|
||||
*
|
||||
* "Full" here is half the size's height (`--group-full`), never `--md-sys-shape-corner-full`'s
|
||||
* 9999px. One element mixes full outer corners with small inner ones, and when a box's radii
|
||||
* add up to more than its side, CSS scales every radius by the same factor: a 9999px corner
|
||||
* beside an 8px one shrank the 8px one to a hundredth of a pixel, so the inner corners drew
|
||||
* square.
|
||||
* 9999px: when a box's radii add up to more than its side, CSS scales every radius by the same
|
||||
* factor, and a 9999px corner beside an 8px one drew the inner corners square.
|
||||
*/
|
||||
|
||||
[data-button-group] {
|
||||
--group-inner: var(--md-sys-shape-corner-sm);
|
||||
--group-inner-pressed: var(--md-sys-shape-corner-xs);
|
||||
--group-full: 1.25rem;
|
||||
--group-square: var(--md-sys-shape-corner-md);
|
||||
--group-square-pressed: var(--md-sys-shape-corner-sm);
|
||||
--group-outer: var(--group-full);
|
||||
}
|
||||
|
||||
[data-button-group][data-size='xs'] { --group-pad: 1rem; --group-grow: 4px; --group-inner: var(--md-sys-shape-corner-xs); --group-inner-pressed: 2px; --group-full: 1rem; }
|
||||
[data-button-group][data-size='sm'] { --group-pad: 1rem; --group-grow: 6px; --group-full: 1.25rem; }
|
||||
[data-button-group][data-size='md'] { --group-pad: 1.5rem; --group-grow: 8px; --group-full: 1.75rem; --group-square: var(--md-sys-shape-corner-lg); --group-square-pressed: var(--md-sys-shape-corner-md); }
|
||||
[data-button-group][data-size='lg'] { --group-pad: 3rem; --group-grow: 16px; --group-inner: var(--md-sys-shape-corner-lg); --group-inner-pressed: var(--md-sys-shape-corner-md); --group-full: 3rem; --group-square: var(--md-sys-shape-corner-xl); --group-square-pressed: var(--md-sys-shape-corner-lg); }
|
||||
[data-button-group][data-size='xl'] { --group-pad: 4rem; --group-grow: 20px; --group-inner: var(--md-sys-shape-corner-lg-increased); --group-inner-pressed: var(--md-sys-shape-corner-lg); --group-full: 4.25rem; --group-square: var(--md-sys-shape-corner-xl); --group-square-pressed: var(--md-sys-shape-corner-lg); }
|
||||
|
||||
/* A square group's ends take the corner its inner edges take — M3's square table for connected
|
||||
groups — and a square standard group hands its buttons the button's own square scale. */
|
||||
[data-button-group][data-shape='square'] { --group-outer: var(--group-inner); }
|
||||
|
||||
[data-button-group='standard'][data-shape='square'] > * {
|
||||
border-radius: var(--group-square);
|
||||
}
|
||||
|
||||
[data-button-group='standard'][data-shape='square'] > :active:not(:disabled, [aria-disabled='true']) {
|
||||
border-radius: var(--group-square-pressed);
|
||||
}
|
||||
|
||||
[data-button-group='standard'][data-shape='square'] > [aria-pressed='true'] {
|
||||
border-radius: var(--group-full);
|
||||
}
|
||||
|
||||
[data-button-group='standard'] > :not([data-md-icon-button]):active:not(:disabled, [aria-disabled='true']) {
|
||||
padding-inline: calc(var(--group-pad) + var(--group-grow));
|
||||
}
|
||||
|
||||
[data-button-group='standard'] > :not([data-md-icon-button]):has(+ :not([data-md-icon-button]):active:not(:disabled, [aria-disabled='true'])) {
|
||||
padding-inline-end: calc(var(--group-pad) - var(--group-grow));
|
||||
}
|
||||
|
||||
[data-button-group='standard'] > :not([data-md-icon-button]):active:not(:disabled, [aria-disabled='true']) + :not([data-md-icon-button]) {
|
||||
padding-inline-start: calc(var(--group-pad) - var(--group-grow));
|
||||
}
|
||||
[data-button-group][data-size='xs'] { --group-full: 1rem; }
|
||||
[data-button-group][data-size='md'] { --group-full: 1.75rem; }
|
||||
[data-button-group][data-size='lg'] { --group-full: 3rem; }
|
||||
[data-button-group][data-size='xl'] { --group-full: 4.25rem; }
|
||||
|
||||
[data-button-group='split'] { --split-inner: var(--md-sys-shape-corner-xs); --split-inner-pressed: var(--md-sys-shape-corner-md); --split-icon: 22px; --split-nudge: 1px; }
|
||||
[data-button-group='split'][data-size='xs'] { --split-inner-pressed: var(--md-sys-shape-corner-sm); }
|
||||
@@ -89,58 +34,30 @@
|
||||
[data-button-group='split'][data-size='lg'] { --split-inner: var(--md-sys-shape-corner-sm); --split-inner-pressed: var(--md-sys-shape-corner-lg-increased); --split-icon: 38px; --split-nudge: 3px; }
|
||||
[data-button-group='split'][data-size='xl'] { --split-inner: var(--md-sys-shape-corner-md); --split-inner-pressed: var(--md-sys-shape-corner-lg-increased); --split-icon: 50px; --split-nudge: 6px; }
|
||||
|
||||
/*
|
||||
* A connected segment at the two smallest sizes is 32px or 40px tall, and an icon-only one is as
|
||||
* narrow as it is tall. M3 names both numbers and tells you not to reduce them: "XS and S
|
||||
* connected button groups have a 48dp target area and a 48dp minimum width". The target is the
|
||||
* `touch-target` utility on the segment itself; the width is here, where a group can reach the
|
||||
* `<x-button>`s a caller put inside it.
|
||||
*/
|
||||
[data-button-group='connected'][data-size='xs'] > *,
|
||||
[data-button-group='connected'][data-size='sm'] > * {
|
||||
min-inline-size: 3rem;
|
||||
}
|
||||
|
||||
/*
|
||||
* Connected segments and split halves take their inner corner from `--group-corner`, so pressing
|
||||
* or selecting changes one variable and the rounded outer corners stay put.
|
||||
*/
|
||||
[data-button-group='connected'] > *,
|
||||
/* The halves take their inner corner from `--group-corner`, so pressing changes one variable and
|
||||
the rounded outer corners stay put. */
|
||||
[data-split] {
|
||||
--group-corner: var(--split-inner);
|
||||
|
||||
border-start-start-radius: var(--group-corner);
|
||||
border-end-start-radius: var(--group-corner);
|
||||
border-start-end-radius: var(--group-corner);
|
||||
border-end-end-radius: var(--group-corner);
|
||||
}
|
||||
|
||||
[data-button-group='connected'] > * {
|
||||
--group-corner: var(--group-inner);
|
||||
}
|
||||
|
||||
[data-button-group='connected'] > :active {
|
||||
--group-corner: var(--group-inner-pressed);
|
||||
}
|
||||
|
||||
[data-split] {
|
||||
--group-corner: var(--split-inner);
|
||||
}
|
||||
|
||||
[data-split]:active {
|
||||
--group-corner: var(--split-inner-pressed);
|
||||
}
|
||||
|
||||
[data-button-group='connected'] > :is([aria-pressed='true'], :has(:checked)),
|
||||
[data-split='trailing'][aria-expanded='true'] {
|
||||
--group-corner: var(--group-full);
|
||||
}
|
||||
|
||||
[data-button-group='connected'] > :first-child,
|
||||
[data-split='leading'] {
|
||||
border-start-start-radius: var(--group-outer);
|
||||
border-end-start-radius: var(--group-outer);
|
||||
}
|
||||
|
||||
[data-button-group='connected'] > :last-child,
|
||||
[data-split='trailing'] {
|
||||
border-start-end-radius: var(--group-outer);
|
||||
border-end-end-radius: var(--group-outer);
|
||||
|
||||
@@ -45,7 +45,7 @@
|
||||
smaller `size` or fewer buttons, or two groups stacked.
|
||||
|
||||
Spacing from the M3 Expressive spec (ButtonGroupSmallTokens: 12px at the small size). The
|
||||
shapes are resources/css/components/groups.css. --}}
|
||||
spacing, shapes and press expansion are resources/css/components/button-group.css. --}}
|
||||
|
||||
@props([
|
||||
'connected' => false,
|
||||
@@ -71,12 +71,12 @@
|
||||
<div
|
||||
role="group"
|
||||
@if ($label) aria-label="{{ $label }}" @endif
|
||||
data-button-group="{{ $connected ? 'connected' : 'standard' }}"
|
||||
data-size="{{ $size }}"
|
||||
data-shape="{{ $shape }}"
|
||||
data-md-button-group="{{ $connected ? 'connected' : 'standard' }}"
|
||||
data-md-size="{{ $size }}"
|
||||
data-md-shape="{{ $shape }}"
|
||||
@if ($selection !== null)
|
||||
data-selection="{{ $selection }}"
|
||||
@if ($required) data-selection-required @endif
|
||||
data-md-selection="{{ $selection }}"
|
||||
@if ($required) data-md-selection-required @endif
|
||||
x-data="{
|
||||
multiple: {{ $multiple ? 'true' : 'false' }},
|
||||
required: {{ $required ? 'true' : 'false' }},
|
||||
@@ -129,13 +129,7 @@
|
||||
x-on:click="press($event)"
|
||||
@unless ($model) x-modelable="value" @endunless
|
||||
@endif
|
||||
{{ $attributes->class([
|
||||
'inline-flex items-center',
|
||||
'gap-0.5' => $connected,
|
||||
'gap-[18px]' => ! $connected && $size === 'xs',
|
||||
'gap-3' => ! $connected && $size === 'sm',
|
||||
'gap-2' => ! $connected && in_array($size, ['md', 'lg', 'xl'], true),
|
||||
]) }}
|
||||
{{ $attributes }}
|
||||
>
|
||||
{{ $slot }}
|
||||
</div>
|
||||
|
||||
@@ -16,6 +16,7 @@ dataset('action components', [
|
||||
'badge',
|
||||
'button',
|
||||
'group',
|
||||
'button-group',
|
||||
]);
|
||||
|
||||
it('draws the component from a stylesheet shaped like every package stylesheet', function (string $name) {
|
||||
|
||||
@@ -2,42 +2,73 @@
|
||||
|
||||
use Livewire\Component;
|
||||
use Livewire\Livewire;
|
||||
use NoNameWeb\LivewireMaterial\Tests\Support\ComponentStylesheet;
|
||||
|
||||
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('groups buttons with the spacing of their size', function (string $size, string $gap) {
|
||||
expect((string) $this->blade("<x-button-group label=\"Views\" size=\"{$size}\"><x-button label=\"Day\" /></x-button-group>"))
|
||||
->toMatch("/<div\\s+role=\"group\"\\s+aria-label=\"Views\"\\s+data-md-button-group=\"standard\"\\s+data-md-size=\"{$size}\"\\s+data-md-shape=\"round\"/")
|
||||
->not->toContain('class=')
|
||||
->and(ComponentStylesheet::read('button-group')->declarations(in_array($size, ['xs', 'sm'], true)
|
||||
? "[data-md-button-group='standard'][data-md-size='{$size}']"
|
||||
: "[data-md-button-group='standard']"))->toBe(['gap' => $gap]);
|
||||
})->with([
|
||||
'xs' => ['xs', '18px'],
|
||||
'sm' => ['sm', '12px'],
|
||||
'md' => ['md', 'var(--md-sys-measurement-space100)'],
|
||||
'lg' => ['lg', 'var(--md-sys-measurement-space100)'],
|
||||
'xl' => ['xl', 'var(--md-sys-measurement-space100)'],
|
||||
]);
|
||||
|
||||
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('connects buttons 2px apart, with M3\'s inner corners', function () {
|
||||
$css = ComponentStylesheet::read('button-group');
|
||||
|
||||
expect((string) $this->blade('<x-button-group connected><x-button label="Day" /></x-button-group>'))->toContain('data-md-button-group="connected"')
|
||||
->and($css->declarations("[data-md-button-group='connected']"))->toBe(['gap' => 'var(--md-sys-measurement-space25)'])
|
||||
->and($css->declarations("[data-md-button-group='connected'] > *"))->toMatchArray([
|
||||
'--md-button-group-corner' => 'var(--md-button-group-inner)',
|
||||
'border-start-start-radius' => 'var(--md-button-group-corner)',
|
||||
])
|
||||
->and($css->declarations("[data-md-button-group='connected'] > :active"))->toBe(['--md-button-group-corner' => 'var(--md-button-group-inner-pressed)'])
|
||||
->and($css->declarations("[data-md-button-group='connected'] > :first-child"))->toBe([
|
||||
'border-start-start-radius' => 'var(--md-button-group-outer)',
|
||||
'border-end-start-radius' => 'var(--md-button-group-outer)',
|
||||
])
|
||||
// Half the height, never 9999px, which would scale the small inner corners away.
|
||||
->and($css->declarations("[data-md-button-group][data-md-size='xl']"))->toMatchArray(['--md-button-group-full' => '68px', '--md-button-group-inner' => 'var(--md-sys-shape-corner-lg-increased)']);
|
||||
});
|
||||
|
||||
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.
|
||||
$css = ComponentStylesheet::read('button-group');
|
||||
|
||||
expect((string) $this->blade('<x-button-group shape="square"><x-button label="Day" /></x-button-group>'))
|
||||
->toContain('data-shape="square"')
|
||||
->toContain('data-md-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"');
|
||||
->toContain('data-md-button-group="connected"')
|
||||
->toContain('data-md-shape="square"')
|
||||
->and((string) $this->blade('<x-button-group shape="oval"><x-button label="Day" /></x-button-group>'))
|
||||
->toContain('data-md-shape="round"')
|
||||
->and($css->declarations("[data-md-button-group='standard'][data-md-shape='square'] > *"))->toBe(['border-radius' => 'var(--md-button-group-square)'])
|
||||
->and($css->declarations("[data-md-button-group='standard'][data-md-shape='square'] > [aria-pressed='true']"))->toBe(['border-radius' => 'var(--md-button-group-full)']);
|
||||
});
|
||||
|
||||
it('widens a pressed label button while its neighbours give way', function () {
|
||||
$css = ComponentStylesheet::read('button-group');
|
||||
|
||||
expect($css->declarations("[data-md-button-group='standard'] > :not([data-md-icon-button]):active:not(:disabled, [aria-disabled='true'])"))
|
||||
->toBe(['padding-inline' => 'calc(var(--md-button-group-pad) + var(--md-button-group-grow))'])
|
||||
->and($css->declarations("[data-md-button-group='standard'] > :not([data-md-icon-button]):active:not(:disabled, [aria-disabled='true']) + :not([data-md-icon-button])"))
|
||||
->toBe(['padding-inline-start' => 'calc(var(--md-button-group-pad) - var(--md-button-group-grow))'])
|
||||
->and($css->declarations("[data-md-button-group][data-md-size='lg']"))->toMatchArray(['--md-button-group-pad' => 'var(--md-sys-measurement-space600)', '--md-button-group-grow' => '16px']);
|
||||
});
|
||||
|
||||
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('data-md-selection="single"')
|
||||
->toContain('data-md-selection-required')
|
||||
->toContain('multiple: false')
|
||||
->toContain('required: true')
|
||||
->toContain('value: null')
|
||||
@@ -49,7 +80,7 @@ it('takes over aria-pressed for a group that carries a selection', function () {
|
||||
->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('data-md-selection')
|
||||
->not->toContain('press($event)');
|
||||
});
|
||||
|
||||
@@ -74,14 +105,14 @@ it('entangles a selection group with the property it binds, and keeps wire:model
|
||||
$html = Livewire::test($component)->assertSet('view', 'week')->html();
|
||||
|
||||
expect($html)
|
||||
->toContain('data-selection="single"')
|
||||
->toContain('data-md-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');
|
||||
expect(ComponentStylesheet::read('button-group')->declarations('[data-md-button-group]'))->toMatchArray(['display' => 'inline-flex'])
|
||||
->not->toHaveKey('flex-wrap');
|
||||
});
|
||||
|
||||
it('marks icon buttons, which keep their width when a group is pressed', function () {
|
||||
|
||||
Reference in New Issue
Block a user