Files
livewire-material/tests/Feature/Components/SurfaceTest.php
T
Andreas Reinhold / reiniandClaude Opus 5 247c596c3a 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>
2026-09-17 19:29:21 +02:00

72 lines
4.3 KiB
PHP

<?php
it('draws a tonal region in surface-container by default, as a div', function () {
$root = layoutRoot((string) $this->blade('<x-surface>Content</x-surface>'));
expect($root)->toMatchArray(['<' => 'div', 'data-md-surface' => 'data-md-surface', 'data-md-level' => 'surface-container'])
->not->toHaveKeys(['data-md-padding', 'data-md-corner', 'data-md-outlined', 'data-md-hide-below', 'data-md-hide-from', 'class', 'style']);
});
it('fills the surface with each of M3\'s surface roles, and the default for any other', function () {
foreach (['surface', 'surface-dim', 'surface-bright', 'surface-container-lowest', 'surface-container-low', 'surface-container', 'surface-container-high', 'surface-container-highest'] as $level) {
expect(layoutRoot((string) $this->blade('<x-surface :level="$level" />', ['level' => $level]))['data-md-level'])->toBe($level);
}
expect(layoutRoot((string) $this->blade('<x-surface level="primary-container" />'))['data-md-level'])->toBe('surface-container');
});
it('pads the surface with a spacing token only, and with none for anything else', function () {
expect(layoutRoot((string) $this->blade('<x-surface padding="space300" />'))['data-md-padding'])->toBe('space300');
foreach (['24px', '3', 'space1000', 'roomy'] as $padding) {
expect(layoutRoot((string) $this->blade('<x-surface :padding="$padding" />', ['padding' => $padding]))['data-md-padding'])->toBe('none');
}
});
it('rounds the surface with a corner token, and leaves it square for anything else', function () {
foreach (['none', 'xs', 'sm', 'md', 'lg', 'lg-increased', 'xl', 'xl-increased', 'xxl', 'full'] as $corner) {
expect(layoutRoot((string) $this->blade('<x-surface :corner="$corner" />', ['corner' => $corner]))['data-md-corner'])->toBe($corner);
}
expect(layoutRoot((string) $this->blade('<x-surface corner="huge" />')))->not->toHaveKey('data-md-corner');
});
it('draws an outline only when asked', function () {
expect(layoutRoot((string) $this->blade('<x-surface outlined />')))->toHaveKey('data-md-outlined')
->and(layoutRoot((string) $this->blade('<x-surface :outlined="false" />')))->not->toHaveKey('data-md-outlined');
});
it('draws the element it is asked for, and a div for one it does not take', function () {
expect(layoutRoot((string) $this->blade('<x-surface as="section">Content</x-surface>'))['<'])->toBe('section')
->and((string) $this->blade('<x-surface as="section">Content</x-surface>'))->toContain('</section>')
->and(layoutRoot((string) $this->blade('<x-surface as="script">Content</x-surface>'))['<'])->toBe('div')
->and(layoutRoot((string) $this->blade('<x-surface as="input" />'))['<'])->toBe('div');
});
it('hides below and from a breakpoint, never below or from compact', function () {
expect(layoutRoot((string) $this->blade('<x-surface hide-below="expanded" hide-from="extra-large" />')))
->toMatchArray(['data-md-hide-below' => 'expanded', 'data-md-hide-from' => 'extra-large']);
foreach (['compact', 'tablet', 'Medium', '840'] as $breakpoint) {
expect(layoutRoot((string) $this->blade('<x-surface :hide-below="$b" :hide-from="$b" />', ['b' => $breakpoint])))
->not->toHaveKeys(['data-md-hide-below', 'data-md-hide-from']);
}
});
it('puts the caller\'s class and style on the surface untouched', function () {
expect(layoutRoot((string) $this->blade('<x-surface class="upload-zone" style="min-height: 12rem" data-test="zone" />')))
->toMatchArray(['class' => 'upload-zone', 'style' => 'min-height: 12rem;', 'data-test' => 'zone']);
});
it('draws the surface from a stylesheet in the layout layer, imported by all.css', function () {
$css = (string) file_get_contents(__DIR__.'/../../../resources/css/layout/surface.css');
expect($css)->toContain("[data-md-surface][data-md-level='surface-container-high'] {")
->toContain('background-color: var(--md-sys-color-surface-container-high);')
->toContain('border: 1px solid var(--md-sys-color-outline-variant);')
->toContain("@import './spacing.css';")
->toContain("@import './visibility.css';")
// A pane on a surface keeps its content off the surface's edge again.
->toContain("[data-md-surface] > * {\n --md-layout-margin: initial;");
});