Cut duplicated and speculative code across the package
An over-engineering audit of the whole tree, applied in five reviewed batches. Behaviour stays the same except where UPGRADE.md says otherwise. PHP: the showcase and error-page stylesheets are prebuilt into resources/dist by bin/stylesheets.mjs, through Vite's own postcss-import (first occurrence kept, the order an application's build gives), instead of Stylesheets::bundle() inlining imports on every request; only the import walk DesignGuard needs stays. SchemeStylesheet::withProfiles() replaces three copies of the scheme-plus-profiles loop, material:scheme leaves spec and contrast checks to the node script that already made them, and the error page's scheme cache, the hashed view namespace, the translations path with no lang/ folder and DesignGuard's 1.x-name hints are gone. JS: the androidx shape port progress.js and both bin scripts each carried lives once in resources/js/shapes.js (the generated SVGs are unchanged); util.js holds ringIndex(), ms(), reopenGuard() and remember(), which were written out several times; listeners are released through AbortController; tooltip.js's hoverPopover() serves the rich tooltip too. CSS: every rule for an element inside the navigation rail queries `--md-navigation-rail-value` instead of repeating the seven collapsed conditions under five media branches; badge, alert, progress, slider and button read one non-inheriting colour-role table (components/color.css); the dialog chrome, the submenu's popover chrome, the chip's state layer and touch target, and the visually-hidden inputs use the shared rules they copied; foundation/tokens.css is folded into foundation.css. Views: Support\Field and Support\Link replace the error-key, bound-value and link-attribute blocks copied into the fields and link components; the timepicker period group, the menu filter and the showcase head are partials; the datepicker's steppers and entry fields are loops; component docblocks no longer restate SKILL.md. Tests and tooling: one dataset-driven ComponentStylesheetsTest replaces four per-group files, DesignGuardTest and the layout-component tests use datasets, browser tests share one ready() helper, CSS parsing lives in ComponentStylesheet alone. docs/audits and the finding IDs citing it are removed, as are pestphp/pest-plugin-laravel, the unused composer scripts and check:font; the lint job runs in the feature job, which now installs node packages so the prebuilt-stylesheet staleness test runs in CI. Feature suite 1177 passed, Chrome browser suite 299 passed. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
471d927e64
commit
247c596c3a
@@ -0,0 +1,68 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Two checks every layout component repeated once per file: the root passes through `as`, a
|
||||
* visibility prop and the caller's own `class`/`style` (after its own style, where it writes one)
|
||||
* and any other attribute; the four with a `gap` prop replace it with a spacing token or drop to
|
||||
* `data-md-gap="none"` for anything else. Folded into one dataset each here, keyed by tag, rather
|
||||
* than kept once per component's own test file — <x-grid>'s style differs (a long per-breakpoint
|
||||
* column list before the caller's own, checked with `$styleEndsWith` instead of the exact string
|
||||
* the others match) and <x-stack>'s gap check has an extra invalid case (`space1000`, a token name
|
||||
* past the scale); both are still one dataset row, not a reason to keep the test itself apart.
|
||||
*/
|
||||
dataset('layout component root attributes', [
|
||||
'feed' => [
|
||||
'<x-feed as="ul" hide-below="medium" class="photos" style="padding-block: 1rem" />',
|
||||
['<' => 'ul', 'data-md-hide-below' => 'medium', 'class' => 'photos', 'style' => '--md-min-item: 240px; padding-block: 1rem;'],
|
||||
],
|
||||
'grid' => [
|
||||
'<x-grid as="ul" hide-below="expanded" class="gallery" style="align-items: start" />',
|
||||
['<' => 'ul', 'data-md-hide-below' => 'expanded', 'class' => 'gallery'],
|
||||
'--md-columns-extra-large: 1; align-items: start;',
|
||||
],
|
||||
'row' => [
|
||||
'<x-row as="nav" hide-from="expanded" class="toolbar-row" style="min-height: 3rem" aria-label="Filters" />',
|
||||
['<' => 'nav', 'data-md-hide-from' => 'expanded', 'class' => 'toolbar-row', 'style' => 'min-height: 3rem;', 'aria-label' => 'Filters'],
|
||||
],
|
||||
'stack' => [
|
||||
'<x-stack as="ul" hide-below="medium" hide-from="large" class="steps" style="max-width: 40rem" />',
|
||||
['<' => 'ul', 'data-md-hide-below' => 'medium', 'data-md-hide-from' => 'large', 'class' => 'steps', 'style' => 'max-width: 40rem;'],
|
||||
],
|
||||
'pane' => [
|
||||
'<x-pane as="section" hide-below="expanded" class="checkout" style="max-width: 50rem" aria-labelledby="checkout-title" />',
|
||||
['<' => 'section', 'data-md-hide-below' => 'expanded', 'class' => 'checkout', 'style' => 'max-width: 50rem;', 'aria-labelledby' => 'checkout-title'],
|
||||
],
|
||||
'list-detail' => [
|
||||
'<x-list-detail as="section" hide-below="medium" class="inbox" style="min-height: 30rem" x-model="chosen" />',
|
||||
['<' => 'section', 'data-md-hide-below' => 'medium', 'class' => 'inbox', 'style' => 'min-height: 30rem;', 'x-model' => 'chosen', 'x-modelable' => 'selected'],
|
||||
],
|
||||
'supporting-pane' => [
|
||||
'<x-supporting-pane as="main" hide-from="extra-large" class="editor" style="min-height: 20rem" />',
|
||||
['<' => 'main', 'data-md-hide-from' => 'extra-large', 'class' => 'editor', 'style' => 'min-height: 20rem;'],
|
||||
],
|
||||
]);
|
||||
|
||||
it('takes the element, the visibility props, and the caller\'s class and style after its own', function (string $blade, array $expected, ?string $styleEndsWith = null) {
|
||||
$root = layoutRoot((string) $this->blade($blade));
|
||||
|
||||
expect($root)->toMatchArray($expected);
|
||||
|
||||
if ($styleEndsWith !== null) {
|
||||
expect($root['style'])->toEndWith($styleEndsWith);
|
||||
}
|
||||
})->with('layout component root attributes');
|
||||
|
||||
dataset('layout component gaps', [
|
||||
'feed' => ['feed', 'space400', ['wide']],
|
||||
'grid' => ['grid', 'space300', ['1.5rem']],
|
||||
'row' => ['row', 'space100', ['tight']],
|
||||
'stack' => ['stack', 'space200', ['16px', 'space1000']],
|
||||
]);
|
||||
|
||||
it('replaces the gap prop with a spacing token, and with none for anything else', function (string $tag, string $token, array $invalid) {
|
||||
expect(layoutRoot((string) $this->blade("<x-{$tag} gap=\"{$token}\" />"))['data-md-gap'])->toBe($token);
|
||||
|
||||
foreach ($invalid as $bad) {
|
||||
expect(layoutRoot((string) $this->blade("<x-{$tag} gap=\"{$bad}\" />"))['data-md-gap'])->toBe('none');
|
||||
}
|
||||
})->with('layout component gaps');
|
||||
Reference in New Issue
Block a user