Keep a switch's track whole beside long text in a row

`[data-md-toggle] { min-width: 0 }` let the switch's root shrink below
its 52px track as a flex item. In `<x-row justify="between">` beside a
paragraph, flex shrinking weighs each item by its width, so the root of
a bare switch gave up a share of its 52px, and a switch with a long
label of its own gave its label a column one word wide and less; the
track, which does not shrink inside the root, ran out of it and past a
card's edge on a phone. `<x-checkbox>`'s root did the same with its box.

Both roots now keep their automatic minimum, the control and, beside a
label, its longest word, so the text beside the control, or the
control's own label, wraps instead. The toggle's docblock and the skill
say so.

A browser test lays a bare switch, a labelled switch and a bare
checkbox beside long text in rows on a 393px window and checks each
control's size and that it stays inside its row, and that the switch's
own label wraps inside its root; it fails without the change in Chrome,
Firefox and Safari.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Andreas Reinhold / reini
2026-09-17 06:55:36 +02:00
co-authored by Claude Opus 5
parent 1885c3e54e
commit cf63f11bfb
6 changed files with 69 additions and 3 deletions
+50
View File
@@ -505,3 +505,53 @@ it('draws the switch\'s handle at SwitchTokens\' sizes and centres, off and on',
->wait(0.7)
->assertScript("{$handle} === '52,32,24,36,16'");
});
it('keeps a switch\'s track and a checkbox\'s box whole beside long text in a row, wrapping the text instead', function () {
Route::middleware('web')->get('/selection-row-probe', fn () => Blade::render(<<<'BLADE'
<!DOCTYPE html>
<html>
<head>
<x-theme-script />
@vite(config('livewire-material.showcase.vite'))
</head>
<body style="background-color: var(--md-sys-color-surface);">
<x-surface outlined padding="space200" style="margin: var(--md-sys-measurement-space200);">
<x-row id="bare-switch-row" justify="between" gap="space200">
<x-stack gap="space50">
<div class="md-type-title-sm">Drop the mark</div>
<p class="md-type-body-sm md-ink-variant">Takes effect on the next sync: your calendar is rewritten with the new names, and a watch keeps the first characters of each.</p>
</x-stack>
<x-toggle id="row-bare-switch" aria-label="Drop the mark from exported workouts" />
</x-row>
<x-row id="labelled-switch-row" justify="between" gap="space200">
<p class="md-type-body-sm md-ink-variant">Takes effect on the next sync: your calendar is rewritten with the new names, and a watch keeps the first characters of each.</p>
<x-toggle id="row-labelled-switch" label="Notifications on this device whenever a session changes" />
</x-row>
<x-row id="bare-check-row" justify="between" gap="space200">
<p class="md-type-body-sm md-ink-variant">Takes effect on the next sync: your calendar is rewritten with the new names, and a watch keeps the first characters of each.</p>
<x-checkbox id="row-bare-check" aria-label="Drop the mark" />
</x-row>
</x-surface>
</body>
</html>
BLADE));
// [the control's drawn width, its end inside its row, the root's end inside its row].
$control = fn (string $input, string $drawing, string $root): string => "(() => {
const drawing = document.getElementById('{$input}').closest('{$drawing}').getBoundingClientRect();
const root = document.getElementById('{$input}').closest('{$root}').getBoundingClientRect();
const row = document.getElementById('{$input}').closest('[data-md-row]').getBoundingClientRect();
return [Math.round(drawing.width), drawing.right <= row.right + 0.5, root.right <= row.right + 0.5];
})()";
// A phone's window: the row is 329px across, less than the text beside each control would take.
$page = visit('/selection-row-probe')->resize(393, 800)->waitForEvent('networkidle')
->assertScript("document.readyState === 'complete'");
expect($page->script($control('row-bare-switch', '[data-md-switch]', '[data-md-toggle]')))->toBe([52, true, true])
->and($page->script($control('row-labelled-switch', '[data-md-switch]', '[data-md-toggle]')))->toBe([52, true, true])
->and($page->script($control('row-bare-check', '[data-md-checkbox-box]', '[data-md-checkbox]')))->toBe([18, true, true])
// The switch's own label wraps inside the switch's root rather than running past it.
->and($page->script("(() => { const label = document.querySelector('[data-md-toggle]:has(#row-labelled-switch) [data-md-selection-label]').getBoundingClientRect(); const root = document.querySelector('[data-md-toggle]:has(#row-labelled-switch)').getBoundingClientRect(); return label.right <= root.right + 0.5 && label.height > 24; })()"))->toBeTrue();
});