Give each canonical layout a page of its own in the Layout section

Plan step 38: the Layout section's Overview keeps its breakpoint
table and live "This window" readout (now a shared
<x-showcase::breakpoint-readout> partial, and the five breakpoint
cards a real range-queried data-md-showcase-breakpoint-card instead
of Tailwind's max-medium:/medium:max-expanded: variants), but the
three canonical layouts move off it onto a page of their own:
/material/layout/{list-detail,supporting-pane,feed}
(ShowcaseController::layout(), routes/showcase.php), each built on
the real component, sharing one <x-section-nav> Sections::
layoutPages() feeds. The search index gains an entry per page with
its own URL, and the component-homes scan now also reads the three
pages' own source so <x-list-detail>, <x-supporting-pane> and
<x-feed> keep a home.

The whole section moves onto the layout components and md-* classes:
no bespoke Tailwind grid or table styling remains.

LayoutTest's browser test now visits the three dedicated pages
instead of one shared /material/layout; a new Feature test asserts
each page renders and is linked from the Layout section.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
This commit is contained in:
Andreas Reinhold / reini
2026-09-15 04:22:38 +02:00
co-authored by Claude Sonnet 5
parent 44a29a8ea2
commit 487176d345
11 changed files with 355 additions and 120 deletions
+43
View File
@@ -276,4 +276,47 @@
width: 20px;
height: 20px;
}
/* Layout: the five breakpoint cards, each lit by the same range query the layout components'
own stylesheets use, so the page proves the breakpoints rather than only describing them. */
[data-md-showcase-breakpoint-card] {
border-radius: var(--md-sys-shape-corner-md);
background-color: var(--md-sys-color-surface-container);
padding: var(--md-sys-measurement-space200);
}
@media (width < 600px) {
[data-md-showcase-breakpoint-card='compact'] {
background-color: var(--md-sys-color-primary-container);
color: var(--md-sys-color-on-primary-container);
}
}
@media (600px <= width < 840px) {
[data-md-showcase-breakpoint-card='medium'] {
background-color: var(--md-sys-color-primary-container);
color: var(--md-sys-color-on-primary-container);
}
}
@media (840px <= width < 1200px) {
[data-md-showcase-breakpoint-card='expanded'] {
background-color: var(--md-sys-color-primary-container);
color: var(--md-sys-color-on-primary-container);
}
}
@media (1200px <= width < 1600px) {
[data-md-showcase-breakpoint-card='large'] {
background-color: var(--md-sys-color-primary-container);
color: var(--md-sys-color-on-primary-container);
}
}
@media (width >= 1600px) {
[data-md-showcase-breakpoint-card='extra-large'] {
background-color: var(--md-sys-color-primary-container);
color: var(--md-sys-color-on-primary-container);
}
}
}
@@ -0,0 +1,18 @@
{{-- The live breakpoint readout: this window's width, and the M3 breakpoint it falls in, read
from the same numbers resources/js/breakpoints.js compares against. Shared by the Layout
section's Overview and its three canonical layout pages (plan step 38). --}}
<x-livewire-material::surface
level="surface-container-low"
padding="space300"
corner="lg"
data-test="window-class"
x-data="{ width: window.innerWidth }"
x-on:resize.window="width = window.innerWidth"
>
<x-livewire-material::row gap="space200" wrap align="baseline">
<span class="md-type-label-lg md-ink-variant">This window</span>
<span class="md-type-emphasized-headline-sm md-tabular" x-text="width + 'px'"></span>
<span class="md-type-title-md" x-text="width < 600 ? 'compact' : width < 840 ? 'medium' : width < 1200 ? 'expanded' : width < 1600 ? 'large' : 'extra-large'"></span>
</x-livewire-material::row>
</x-livewire-material::surface>
@@ -0,0 +1,41 @@
{{-- The feed canonical layout on a page of its own (plan step 38): the same working demo the
Layout section's Overview linked from its own navigation, built on the real <x-feed>, with
the live breakpoint readout the Overview page has. --}}
@extends('livewire-material::showcase.layout')
@php
$code = <<<'BLADE'
<x-feed min-item="220px">
<x-card title="Harbour at dawn" subtitle="12 photos" variant="outlined">The boats before the ferry.</x-card>
<x-card title="Old town" subtitle="31 photos" variant="outlined">Every lane, twice.</x-card>
<x-card title="Lake swim" subtitle="4 videos" variant="outlined">Colder than it looks.</x-card>
<x-card title="Market" subtitle="18 photos" variant="outlined">Cheese, mostly.</x-card>
<x-card title="The long walk" subtitle="9 photos" variant="outlined">Up to the ridge and back.</x-card>
<x-card title="Train home" subtitle="3 photos" variant="outlined">Asleep by the second stop.</x-card>
</x-feed>
BLADE;
@endphp
@section('content')
<x-livewire-material::pane width="wide" data-md-showcase-page>
<x-livewire-material::stack gap="space300">
<x-livewire-material::stack as="header" gap="space50">
<p class="md-type-label-lg md-ink-variant">Foundations</p>
<h1 class="md-type-headline-lg">Layout</h1>
</x-livewire-material::stack>
<x-livewire-material::section-nav :items="array_values(\NoNameWeb\LivewireMaterial\Showcase\Sections::layoutPages())" label="Layout pages" />
<x-showcase::breakpoint-readout />
<p class="md-type-body-md md-ink-variant">
M3's feed: cards or items to browse, in columns that multiply as the room grows news, photos,
posts. One column below 600px, then as many equal columns of at least <code>min-item</code> as the
feed has room for.
</p>
<x-showcase::example title="Feed" :code="$code" stack />
</x-livewire-material::stack>
</x-livewire-material::pane>
@endsection
@@ -0,0 +1,56 @@
{{-- The list-detail canonical layout on a page of its own (plan step 38): the same working demo
the Layout section's Overview linked from its own navigation, built on the real
<x-list-detail>, with the live breakpoint readout the Overview page has. --}}
@extends('livewire-material::showcase.layout')
@php
$code = <<<'BLADE'
<x-surface level="surface" corner="lg" outlined x-data="{ message: null }" style="overflow: clip">
<x-list-detail x-model="message">
<x-slot:list>
<x-pane title="Inbox" heading="h3" :sticky="false">
<x-list label="Messages">
<x-list-item title="Anna Müller" description="The photos from Saturday" avatar="AM" link="#example-list-detail" no-wire-navigate x-on:click.prevent="selected = 1" x-bind:aria-current="selected === 1 ? 'true' : null" />
<x-list-item title="Ben Keller" description="Contract, second draft" avatar="BK" link="#example-list-detail" no-wire-navigate x-on:click.prevent="selected = 2" x-bind:aria-current="selected === 2 ? 'true' : null" />
<x-list-item title="Chiara Rossi" description="Train times for Friday" avatar="CR" link="#example-list-detail" no-wire-navigate x-on:click.prevent="selected = 3" x-bind:aria-current="selected === 3 ? 'true' : null" />
</x-list>
</x-pane>
</x-slot:list>
<x-slot:detail>
<x-pane heading="h3" back :sticky="false">
<x-stack gap="space200">
<p class="md-type-body-lg md-ink-variant" x-show="! selected">Select a message to read it.</p>
<p class="md-type-headline-sm" x-show="selected" x-text="{ 1: 'The photos from Saturday', 2: 'Contract, second draft', 3: 'Train times for Friday' }[selected]"></p>
<p class="md-type-body-lg" x-show="selected">Below 840px the list gives way to this message, with a back button that returns you — and your focus — to the list. From 840px both panes show, the list 360px wide and 412px from 1200px.</p>
</x-stack>
</x-pane>
</x-slot:detail>
</x-list-detail>
</x-surface>
BLADE;
@endphp
@section('content')
<x-livewire-material::pane width="wide" data-md-showcase-page>
<x-livewire-material::stack gap="space300">
<x-livewire-material::stack as="header" gap="space50">
<p class="md-type-label-lg md-ink-variant">Foundations</p>
<h1 class="md-type-headline-lg">Layout</h1>
</x-livewire-material::stack>
<x-livewire-material::section-nav :items="array_values(\NoNameWeb\LivewireMaterial\Showcase\Sections::layoutPages())" label="Layout pages" />
<x-showcase::breakpoint-readout />
<p class="md-type-body-md md-ink-variant">
M3's list-detail: a list, and the detail of what is selected in it an inbox and a message, folders
and a file, settings and a category. Below 840px the list gives way to the detail with a back button;
from 840px both panes show, the list a fixed 360px (412px from 1200px) and the detail flexible.
</p>
<x-showcase::example title="List-detail" :code="$code" stack />
</x-livewire-material::stack>
</x-livewire-material::pane>
@endsection
@@ -0,0 +1,54 @@
{{-- The supporting-pane canonical layout on a page of its own (plan step 38): the same working
demo the Layout section's Overview linked from its own navigation, built on the real
<x-supporting-pane>, with the live breakpoint readout the Overview page has. --}}
@extends('livewire-material::showcase.layout')
@php
$code = <<<'BLADE'
<x-supporting-pane label="Comments" compact="sheet">
<x-slot:main>
<x-surface level="surface" corner="lg" style="overflow: clip">
<x-pane title="Trip proposal" subtitle="Draft · edited today" heading="h3" :sticky="false">
<x-stack gap="space200" style="padding-block-end: var(--md-sys-measurement-space300)">
<p class="md-type-body-lg">Three days by the lake in June: the train on Friday morning, two nights at the harbour, and a walk round the old town on Sunday before the train home.</p>
<p class="md-type-body-lg">From 840px the comments sit beside the proposal, 360px wide (412px from 1200px). Below it they are a bottom sheet docked to the window: press its handle to read them.</p>
</x-stack>
</x-pane>
</x-surface>
</x-slot:main>
<x-slot:supporting>
<x-surface level="surface-container-low" corner="lg" padding="space200">
<x-list label="Comments">
<x-list-item title="Anna Müller" description="Friday works for me." avatar="AM" />
<x-list-item title="Ben Keller" description="Can we stay a third night?" avatar="BK" />
</x-list>
</x-surface>
</x-slot:supporting>
</x-supporting-pane>
BLADE;
@endphp
@section('content')
<x-livewire-material::pane width="wide" data-md-showcase-page>
<x-livewire-material::stack gap="space300">
<x-livewire-material::stack as="header" gap="space50">
<p class="md-type-label-lg md-ink-variant">Foundations</p>
<h1 class="md-type-headline-lg">Layout</h1>
</x-livewire-material::stack>
<x-livewire-material::section-nav :items="array_values(\NoNameWeb\LivewireMaterial\Showcase\Sections::layoutPages())" label="Layout pages" />
<x-showcase::breakpoint-readout />
<p class="md-type-body-md md-ink-variant">
M3's supporting pane: a focus pane, and beside it what only means something in relation to it the
comments on a document, the details of a video. Below 840px it moves under the focus pane or into a
bottom sheet; from 840px it sits beside it, a fixed 360px (412px from 1200px).
</p>
<x-showcase::example title="Supporting pane" :code="$code" stack />
</x-livewire-material::stack>
</x-livewire-material::pane>
@endsection
@@ -1,71 +1,13 @@
{{-- M3's window size classes, live: the one this window is in, what each one changes, and the
variants that key on them. The five cards light up through the variants themselves, so the
page proves the compiled breakpoints rather than describing them.
{{-- M3's breakpoints, live: the one this window is in, what each one changes, and the cards that
light up through real media queries, so the page proves the breakpoints rather than only
describing them.
Then the layout components, and an example of each canonical layout — list-detail, supporting
pane, feed — written as an application writes them, with the layout components and the md-*
text classes and no Tailwind. Resize the window to watch each one change at its breakpoints. --}}
Then the layout components, and the way on to the three canonical layouts — list-detail,
supporting pane, feed — each on a page of its own (plan step 38), reached through the
section navigation below. --}}
@php
$examples = [
'List-detail' => <<<'BLADE'
<x-surface level="surface" corner="lg" outlined x-data="{ message: null }" style="overflow: clip">
<x-list-detail x-model="message">
<x-slot:list>
<x-pane title="Inbox" heading="h3" :sticky="false">
<x-list label="Messages">
<x-list-item title="Anna Müller" description="The photos from Saturday" avatar="AM" link="#example-list-detail" no-wire-navigate x-on:click.prevent="selected = 1" x-bind:aria-current="selected === 1 ? 'true' : null" />
<x-list-item title="Ben Keller" description="Contract, second draft" avatar="BK" link="#example-list-detail" no-wire-navigate x-on:click.prevent="selected = 2" x-bind:aria-current="selected === 2 ? 'true' : null" />
<x-list-item title="Chiara Rossi" description="Train times for Friday" avatar="CR" link="#example-list-detail" no-wire-navigate x-on:click.prevent="selected = 3" x-bind:aria-current="selected === 3 ? 'true' : null" />
</x-list>
</x-pane>
</x-slot:list>
<x-slot:detail>
<x-pane heading="h3" back :sticky="false">
<x-stack gap="space200">
<p class="md-type-body-lg md-ink-variant" x-show="! selected">Select a message to read it.</p>
<p class="md-type-headline-sm" x-show="selected" x-text="{ 1: 'The photos from Saturday', 2: 'Contract, second draft', 3: 'Train times for Friday' }[selected]"></p>
<p class="md-type-body-lg" x-show="selected">Below 840px the list gives way to this message, with a back button that returns you — and your focus — to the list. From 840px both panes show, the list 360px wide and 412px from 1200px.</p>
</x-stack>
</x-pane>
</x-slot:detail>
</x-list-detail>
</x-surface>
BLADE,
'Supporting pane' => <<<'BLADE'
<x-supporting-pane label="Comments" compact="sheet">
<x-slot:main>
<x-surface level="surface" corner="lg" style="overflow: clip">
<x-pane title="Trip proposal" subtitle="Draft · edited today" heading="h3" :sticky="false">
<x-stack gap="space200" style="padding-block-end: var(--md-sys-measurement-space300)">
<p class="md-type-body-lg">Three days by the lake in June: the train on Friday morning, two nights at the harbour, and a walk round the old town on Sunday before the train home.</p>
<p class="md-type-body-lg">From 840px the comments sit beside the proposal, 360px wide (412px from 1200px). Below it they are a bottom sheet docked to the window: press its handle to read them.</p>
</x-stack>
</x-pane>
</x-surface>
</x-slot:main>
<x-slot:supporting>
<x-surface level="surface-container-low" corner="lg" padding="space200">
<x-list label="Comments">
<x-list-item title="Anna Müller" description="Friday works for me." avatar="AM" />
<x-list-item title="Ben Keller" description="Can we stay a third night?" avatar="BK" />
</x-list>
</x-surface>
</x-slot:supporting>
</x-supporting-pane>
BLADE,
'Feed' => <<<'BLADE'
<x-feed min-item="220px">
<x-card title="Harbour at dawn" subtitle="12 photos" variant="outlined">The boats before the ferry.</x-card>
<x-card title="Old town" subtitle="31 photos" variant="outlined">Every lane, twice.</x-card>
<x-card title="Lake swim" subtitle="4 videos" variant="outlined">Colder than it looks.</x-card>
<x-card title="Market" subtitle="18 photos" variant="outlined">Cheese, mostly.</x-card>
<x-card title="The long walk" subtitle="9 photos" variant="outlined">Up to the ridge and back.</x-card>
<x-card title="Train home" subtitle="3 photos" variant="outlined">Asleep by the second stop.</x-card>
</x-feed>
BLADE,
'Surfaces, stacks, rows and grids' => <<<'BLADE'
<x-stack gap="space300">
<x-grid :columns="['compact' => 1, 'medium' => 2, 'expanded' => 4]" gap="space200">
@@ -88,59 +30,48 @@
];
@endphp
<section id="layout" class="scroll-mt-24 space-y-6">
<h2 class="type-headline-md">Layout</h2>
<x-livewire-material::stack as="section" id="layout" gap="space300">
<h2 class="md-type-headline-md">Layout</h2>
<p class="max-w-3xl type-body-md text-on-surface-variant">
Widths are M3's window size classes and nothing else: compact below 600px, then <code>medium:</code> 600,
<code>expanded:</code> 840, <code>large:</code> 1200 and <code>extra-large:</code> 1600, with
<code>max-medium:</code> and friends for "below". Tailwind's <code>sm:</code>…<code>2xl:</code> compile to
nothing. Scripts read the same numbers from <code>resources/js/breakpoints.js</code>.
<p class="md-type-body-md md-ink-variant">
Widths are M3's breakpoints and nothing else: compact below 600px, then medium 600, expanded 840, large 1200
and extra-large 1600. Scripts read the same numbers from <code>resources/js/breakpoints.js</code>.
</p>
<div
x-data="{ width: window.innerWidth }"
x-on:resize.window="width = window.innerWidth"
class="flex flex-wrap items-baseline gap-x-4 gap-y-1 rounded-corner-lg bg-surface-container-low p-6"
data-test="window-class"
>
<span class="type-label-lg text-on-surface-variant">This window</span>
<span class="type-emphasized-headline-sm tabular-nums" x-text="width + 'px'"></span>
<span class="type-title-md" x-text="width < 600 ? 'compact' : width < 840 ? 'medium' : width < 1200 ? 'expanded' : width < 1600 ? 'large' : 'extra-large'"></span>
</div>
<x-showcase::breakpoint-readout />
<div class="grid gap-3 medium:grid-cols-5">
<div class="rounded-corner-md bg-surface-container p-4 max-medium:bg-primary-container max-medium:text-on-primary-container">
<p class="type-title-sm">Compact</p>
<p class="type-body-sm">below 600px</p>
<x-livewire-material::grid :columns="['compact' => 1, 'medium' => 5]" gap="space200">
<div data-md-showcase-breakpoint-card="compact">
<p class="md-type-title-sm">Compact</p>
<p class="md-type-body-sm">below 600px</p>
</div>
<div class="rounded-corner-md bg-surface-container p-4 medium:max-expanded:bg-primary-container medium:max-expanded:text-on-primary-container">
<p class="type-title-sm">Medium</p>
<p class="type-body-sm">600839px</p>
<div data-md-showcase-breakpoint-card="medium">
<p class="md-type-title-sm">Medium</p>
<p class="md-type-body-sm">600839px</p>
</div>
<div class="rounded-corner-md bg-surface-container p-4 expanded:max-large:bg-primary-container expanded:max-large:text-on-primary-container">
<p class="type-title-sm">Expanded</p>
<p class="type-body-sm">8401199px</p>
<div data-md-showcase-breakpoint-card="expanded">
<p class="md-type-title-sm">Expanded</p>
<p class="md-type-body-sm">8401199px</p>
</div>
<div class="rounded-corner-md bg-surface-container p-4 large:max-extra-large:bg-primary-container large:max-extra-large:text-on-primary-container">
<p class="type-title-sm">Large</p>
<p class="type-body-sm">12001599px</p>
<div data-md-showcase-breakpoint-card="large">
<p class="md-type-title-sm">Large</p>
<p class="md-type-body-sm">12001599px</p>
</div>
<div class="rounded-corner-md bg-surface-container p-4 extra-large:bg-primary-container extra-large:text-on-primary-container">
<p class="type-title-sm">Extra-large</p>
<p class="type-body-sm">from 1600px</p>
<div data-md-showcase-breakpoint-card="extra-large">
<p class="md-type-title-sm">Extra-large</p>
<p class="md-type-body-sm">from 1600px</p>
</div>
</div>
</x-livewire-material::grid>
<div class="overflow-x-auto rounded-corner-lg border border-structure">
<table class="w-full type-body-md" data-md-table>
<x-livewire-material::surface corner="lg" outlined style="overflow-x: auto">
<x-livewire-material::table>
<thead>
<tr>
<th class="text-start">Class</th>
<th class="text-start">Navigation</th>
<th class="text-start">Panes</th>
<th class="text-start">Dialogs and sheets</th>
<th class="text-start">Margin</th>
<th>Class</th>
<th>Navigation</th>
<th>Panes</th>
<th>Dialogs and sheets</th>
<th>Margin</th>
</tr>
</thead>
<tbody>
@@ -180,10 +111,10 @@
<td>24px</td>
</tr>
</tbody>
</table>
</div>
</x-livewire-material::table>
</x-livewire-material::surface>
<p class="max-w-3xl type-body-md text-on-surface-variant">
<p class="md-type-body-md md-ink-variant">
<code>&lt;x-scaffold&gt;</code> does all of this: resize this showcase and watch the bar, the rail and the
content margin (<code>--material-margin</code>) change at 600, 840 and 1200px. A visitor who has chosen a rail
width keeps it; until then the rail follows the class.
@@ -209,7 +140,13 @@
</x-livewire-material::stack>
</x-livewire-material::stack>
<x-livewire-material::stack gap="space200">
<h3 class="md-type-title-lg">Each canonical layout, on a page of its own</h3>
<x-livewire-material::section-nav :items="array_values(\NoNameWeb\LivewireMaterial\Showcase\Sections::layoutPages())" label="Layout pages" />
</x-livewire-material::stack>
@foreach ($examples as $title => $code)
<x-showcase::example :$title :$code stack />
@endforeach
</section>
</x-livewire-material::stack>
+6
View File
@@ -32,6 +32,12 @@ Route::get('mail', [ShowcasePageController::class, 'mail'])->name('mail');
Route::get('search.json', [ShowcaseController::class, 'search'])->name('search');
// The Layout section's three canonical layouts (plan step 38), each a page of its own rather
// than an anchor on the Layout section's Overview (`{section}` below).
Route::get('layout/{page}', [ShowcaseController::class, 'layout'])
->whereIn('page', ['list-detail', 'supporting-pane', 'feed'])
->name('layout');
Route::get('{section}', [ShowcaseController::class, 'section'])
->whereIn('section', array_keys(Sections::all()))
->name('section');
@@ -24,6 +24,19 @@ class ShowcaseController
]);
}
/**
* One of the Layout section's own pages (plan step 38): a canonical layout on a page of its
* own, sharing the Layout section's rail entry and title.
*/
public function layout(string $page): View
{
return view('livewire-material::showcase.layout.'.$page, [
'sections' => Sections::all(),
'section' => 'layout',
'title' => Sections::layoutPages()[$page]['title'].' · Layout',
]);
}
/**
* The search's index, fetched when the search is first focused rather than written into every page.
*/
+41
View File
@@ -40,6 +40,19 @@ class Sections
}
}
// The Layout section's three canonical layouts (plan step 38): each is a page of its own
// rather than an anchor on the Overview, so its search entry carries its own URL; the
// page's own source joins the Layout section's for the component-homes scan below, so
// <x-list-detail>, <x-supporting-pane> and <x-feed> still find a home.
foreach (static::layoutPages() as $key => $page) {
if ($key === 'layout') {
continue;
}
$entries[] = ['title' => $page['title'], 'kind' => 'Example', 'context' => 'Layout', 'url' => $page['url'].'#'.static::anchor($page['title']), 'keywords' => ''];
$sources['layout'] .= (string) file_get_contents(static::layoutPagePath($key));
}
// A section that names a component in its introduction is its home; otherwise the first that uses it.
foreach (['/&lt;x-([a-z0-9-]+)&gt;/', '/<x-(?:livewire-material::)?([a-z0-9-]+)(?=[\\s\\/>])/'] as $pattern) {
foreach ($sources as $key => $source) {
@@ -76,6 +89,34 @@ class Sections
return dirname(__DIR__, 2).'/resources/views/showcase/sections/'.$key.'.blade.php';
}
/**
* The Layout section's own pages (plan step 38): the overview and the three canonical
* layouts, each on a page of its own, sharing one `<x-section-nav>` and the live breakpoint
* readout rather than three anchors on the Overview. They are not top-level destinations of
* their own — `Sections::all()` still has one `layout` entry for the rail and the overview
* grid — so this is their own small index instead.
*
* @return array<string, array{title: string, icon: string, url: string}>
*/
public static function layoutPages(): array
{
return [
'layout' => ['title' => 'Overview', 'icon' => 'devices', 'url' => route('livewire-material.section', 'layout', false)],
'list-detail' => ['title' => 'List-detail', 'icon' => 'view_sidebar', 'url' => route('livewire-material.layout', 'list-detail', false)],
'supporting-pane' => ['title' => 'Supporting pane', 'icon' => 'splitscreen', 'url' => route('livewire-material.layout', 'supporting-pane', false)],
'feed' => ['title' => 'Feed', 'icon' => 'view_agenda', 'url' => route('livewire-material.layout', 'feed', false)],
];
}
/**
* One of the Layout section's own pages' view file (`layoutPages()`'s keys but `layout`
* itself, which is `path('layout')`).
*/
public static function layoutPagePath(string $key): string
{
return dirname(__DIR__, 2).'/resources/views/showcase/layout/'.$key.'.blade.php';
}
/**
* @return array<string, array{title: string, icon: string, group: string, description: string}>
*/
+19 -11
View File
@@ -529,25 +529,33 @@ it('draws no second margin for a pane or a canonical layout inside the scaffold\
->assertScript(layoutStyle('#pane [data-md-pane-body]', 'paddingLeft')." === '0px'");
});
it('shows each canonical layout working on the showcase\'s Layout page', function () {
$example = fn (string $title): string => "#example-{$title}";
$page = layoutReady(visit('/material/layout')->resize(1280, 900))
it('shows each canonical layout working on its own Layout page', function () {
$listDetail = layoutReady(visit('/material/layout/list-detail')->resize(1280, 900))
->assertScript("document.querySelectorAll('#example-list-detail [data-md-list-detail-pane]').length === 2")
->assertScript("[...document.querySelectorAll('#example-list-detail [data-md-list-detail-pane]')].every((pane) => pane.checkVisibility())")
->assertScript(layoutRect($example('supporting-pane').' [data-md-supporting-pane-supporting]', 'top').' === '.layoutRect($example('supporting-pane').' [data-md-supporting-pane-main]', 'top'))
->assertScript(layoutColumns($example('feed').' [data-md-feed]').' > 1');
->assertScript("[...document.querySelectorAll('#example-list-detail [data-md-list-detail-pane]')].every((pane) => pane.checkVisibility())");
$page->click('#example-list-detail a:has-text("Ben Keller")')
$listDetail->click('#example-list-detail a:has-text("Ben Keller")')
->assertSeeIn('#example-list-detail [data-md-list-detail-pane="detail"]', 'Contract, second draft')
->assertNoJavaScriptErrors();
$compact = layoutReady(visit('/material/layout')->resize(599, 900));
$compactListDetail = layoutReady(visit('/material/layout/list-detail')->resize(599, 900));
$compact->click('#example-list-detail a:has-text("Chiara Rossi")')
$compactListDetail->click('#example-list-detail a:has-text("Chiara Rossi")')
->assertSeeIn('#example-list-detail [data-md-list-detail-pane="detail"]', 'Train times for Friday')
->assertScript("! document.querySelector('#example-list-detail [data-md-list-detail-pane=\"list\"]').checkVisibility()")
->assertScript(layoutColumns($example('feed').' [data-md-feed]').' === 1')
->assertNoJavaScriptErrors();
layoutReady(visit('/material/layout/supporting-pane')->resize(1280, 900))
->assertScript(layoutRect('#example-supporting-pane [data-md-supporting-pane-supporting]', 'top').' === '.layoutRect('#example-supporting-pane [data-md-supporting-pane-main]', 'top'));
layoutReady(visit('/material/layout/supporting-pane')->resize(599, 900))
->assertScript("document.querySelector('#example-supporting-pane [data-md-supporting-pane-handle]').checkVisibility()")
->assertNoJavaScriptErrors();
layoutReady(visit('/material/layout/feed')->resize(1280, 900))
->assertScript(layoutColumns('#example-feed [data-md-feed]').' > 1');
layoutReady(visit('/material/layout/feed')->resize(599, 900))
->assertScript(layoutColumns('#example-feed [data-md-feed]').' === 1')
->assertNoJavaScriptErrors();
});
+18
View File
@@ -45,6 +45,24 @@ it('gives every section a page of its own, linked from the overview and the rail
$this->get('/material/not-a-section')->assertNotFound();
});
it('gives each canonical layout a page of its own, linked from the Layout section', function () {
foreach (['list-detail', 'supporting-pane', 'feed'] as $page) {
test()->withoutVite()->get("/material/layout/{$page}")
->assertOk()
->assertSee('id="example-'.$page.'"', false)
// Its own navigation reaches the other three pages, including itself.
->assertSee(route('livewire-material.layout', 'list-detail', false), false)
->assertSee(route('livewire-material.layout', 'supporting-pane', false), false)
->assertSee(route('livewire-material.layout', 'feed', false), false);
}
test()->withoutVite()->get('/material/layout')
->assertOk()
->assertSee(route('livewire-material.layout', 'list-detail', false), false)
->assertSee(route('livewire-material.layout', 'supporting-pane', false), false)
->assertSee(route('livewire-material.layout', 'feed', false), false);
});
it('indexes every section, example and component for the search, each linking to a place that exists', function () {
$index = collect($this->getJson('/material/search.json')->assertOk()->json());