Fold layout.css and components.css into all.css
Plan step 37: all.css replaces the two interim import lists with one entry for an application that wants everything — the foundation, every layout stylesheet and every component stylesheet, grouped under the same block comments components.css used, plus a Layout block. It also directly imports the three files nothing imported by name before (layout/spacing.css, layout/visibility.css, components/selection.css), so every file under components/ and layout/ is now one @import away. Every test that read a block of components.css or layout.css now reads the matching block of all.css through one shared helper (allCssBlock(), in tests/Pest.php so it loads for any test run) instead of repeating the same substr() search in each file. StylesheetsTest.php's shape, Tailwind-free and breakpoint checks, previously run twice (once for the foundation, once for the layout tree), now run once over the whole tree all.css reaches, since every component and layout stylesheet is plain CSS after step 36; a new test asserts all.css imports everything under components/ and layout/ exactly once. The Workbench imports all.css in place of layout.css and components.css. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
04271bf227
commit
925d9a4439
@@ -1,12 +1,40 @@
|
|||||||
/*
|
/*
|
||||||
* The component stylesheets already written without Tailwind (plan step 36), while the rest are
|
* Livewire Material for an application that wants everything: the foundation, every layout
|
||||||
* still in tailwind.css. Each lives in `material.components`, so it can be imported anywhere in
|
* stylesheet and every component stylesheet, each imported once. The blocks below are the same
|
||||||
* the entry: the layer statement, not the import order, decides where its rules rank. Step 37
|
* groups `components.css` and `layout.css` used before this file replaced both of them (plan step
|
||||||
* replaces this file with all.css. One block per stream, so parallel rewrites merge cleanly.
|
* 37) — one block per audit stream, so a stylesheet still lands in a predictable place — plus the
|
||||||
|
* Layout block those two files didn't share.
|
||||||
|
*
|
||||||
|
* This is not the recommended way to bring the package's CSS in: importing `all.css` pulls in
|
||||||
|
* every component's rules whether an application's views render them or not. The recommended path
|
||||||
|
* is `foundation.css` (the one required import) followed by only the component stylesheets a
|
||||||
|
* view actually renders — each component file already imports the stylesheets of the components
|
||||||
|
* it draws, so the list an application writes stays short. `all.css` is for the showcase, for an
|
||||||
|
* error page rendered without a build, and for an application that would rather not track
|
||||||
|
* per-component imports at all.
|
||||||
|
*
|
||||||
|
* Same shape as every package stylesheet: the layer statement first, then plain imports, nothing
|
||||||
|
* else at the top level — an `@import` cannot sit inside a `@layer` block, and `layer()` on it
|
||||||
|
* would nest the layer a second time inside a file that already declares it.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@layer material.reset, material.tokens, material.base, material.layout, material.components, material.text, material.visibility;
|
@layer material.reset, material.tokens, material.base, material.layout, material.components, material.text, material.visibility;
|
||||||
|
|
||||||
|
@import './foundation.css';
|
||||||
|
|
||||||
|
/* Layout */
|
||||||
|
@import './layout/spacing.css';
|
||||||
|
@import './layout/visibility.css';
|
||||||
|
@import './layout/stack.css';
|
||||||
|
@import './layout/row.css';
|
||||||
|
@import './layout/grid.css';
|
||||||
|
@import './layout/surface.css';
|
||||||
|
@import './layout/pane.css';
|
||||||
|
@import './layout/list-detail.css';
|
||||||
|
@import './layout/supporting-pane.css';
|
||||||
|
@import './layout/feed.css';
|
||||||
|
@import './layout/scaffold.css';
|
||||||
|
|
||||||
/* Foundation components */
|
/* Foundation components */
|
||||||
@import './components/icon.css';
|
@import './components/icon.css';
|
||||||
@import './components/shape.css';
|
@import './components/shape.css';
|
||||||
@@ -41,6 +69,7 @@
|
|||||||
@import './components/textarea.css';
|
@import './components/textarea.css';
|
||||||
@import './components/select.css';
|
@import './components/select.css';
|
||||||
@import './components/file.css';
|
@import './components/file.css';
|
||||||
|
@import './components/selection.css';
|
||||||
@import './components/checkbox.css';
|
@import './components/checkbox.css';
|
||||||
@import './components/radio.css';
|
@import './components/radio.css';
|
||||||
@import './components/toggle.css';
|
@import './components/toggle.css';
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
/*
|
|
||||||
* The layout components (plan step 35): the scaffold, panes, the canonical layouts, surfaces and
|
|
||||||
* the arrangements inside a pane, each in `material.layout`, with the visibility props in
|
|
||||||
* `material.visibility`. Step 37 folds this file into all.css.
|
|
||||||
*/
|
|
||||||
|
|
||||||
@layer material.reset, material.tokens, material.base, material.layout, material.components, material.text, material.visibility;
|
|
||||||
|
|
||||||
@import './layout/stack.css';
|
|
||||||
@import './layout/row.css';
|
|
||||||
@import './layout/grid.css';
|
|
||||||
@import './layout/surface.css';
|
|
||||||
@import './layout/pane.css';
|
|
||||||
@import './layout/list-detail.css';
|
|
||||||
@import './layout/supporting-pane.css';
|
|
||||||
@import './layout/feed.css';
|
|
||||||
@import './layout/scaffold.css';
|
|
||||||
@@ -10,13 +10,13 @@ use NoNameWeb\LivewireMaterial\Tests\Support\ViewClasses;
|
|||||||
* classes (tests/Support/ViewClasses.php), and each has a stylesheet in
|
* classes (tests/Support/ViewClasses.php), and each has a stylesheet in
|
||||||
* `material.components` that imports the stylesheets of the components its view renders, writes
|
* `material.components` that imports the stylesheets of the components its view renders, writes
|
||||||
* its values from the tokens and its breakpoints as px range queries, and is imported from the
|
* its values from the tokens and its breakpoints as px range queries, and is imported from the
|
||||||
* "Actions and communication" block of components.css.
|
* "Actions and communication" block of all.css.
|
||||||
*
|
*
|
||||||
* `icon` and `shape` are the two foundation components (`<x-icon>`, `<x-shape>`) — no group's view
|
* `icon` and `shape` are the two foundation components (`<x-icon>`, `<x-shape>`) — no group's view
|
||||||
* renders them exclusively, so they were in no group's dataset at all. They follow the same shape,
|
* renders them exclusively, so they were in no group's dataset at all. They follow the same shape,
|
||||||
* token and view-class rules as every other component, so they run with this group's checks too;
|
* token and view-class rules as every other component, so they run with this group's checks too;
|
||||||
* only "is imported from the actions and communication block" needs to read the two of them from
|
* only "is imported from the actions and communication block" needs to read the two of them from
|
||||||
* components.css's first block ("Foundation components") instead, since that is where they sit.
|
* all.css's first block ("Foundation components") instead, since that is where they sit.
|
||||||
*/
|
*/
|
||||||
dataset('action components', [
|
dataset('action components', [
|
||||||
'icon',
|
'icon',
|
||||||
@@ -95,16 +95,8 @@ it('takes its values from the tokens and its breakpoints in px', function (strin
|
|||||||
}
|
}
|
||||||
})->with('action components');
|
})->with('action components');
|
||||||
|
|
||||||
it('is imported from the actions and communication block of components.css, or icon and shape from the foundation block before it', function (string $name) {
|
it('is imported from the actions and communication block of all.css, or icon and shape from the foundation block before it', function (string $name) {
|
||||||
$components = File::get(__DIR__.'/../../../resources/css/components.css');
|
$block = allCssBlock(in_array($name, ['icon', 'shape'], true) ? 'Foundation components' : 'Actions and communication');
|
||||||
|
|
||||||
if (in_array($name, ['icon', 'shape'], true)) {
|
|
||||||
$block = substr($components, (int) strpos($components, '/* Foundation components */'));
|
|
||||||
$block = substr($block, 0, (int) strpos($block, '/* Actions and communication */'));
|
|
||||||
} else {
|
|
||||||
$block = substr($components, (int) strpos($components, '/* Actions and communication */'));
|
|
||||||
$block = substr($block, 0, (int) strpos($block, '/* Inputs, selection and data */'));
|
|
||||||
}
|
|
||||||
|
|
||||||
expect($block)->toContain("@import './components/{$name}.css';");
|
expect($block)->toContain("@import './components/{$name}.css';");
|
||||||
})->with('action components');
|
})->with('action components');
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ it('draws a chip at Compose\'s size, corner, type and padding from its styleshee
|
|||||||
// 16px beside a label and 8px beside an icon, each 1px short for the border.
|
// 16px beside a label and 8px beside an icon, each 1px short for the border.
|
||||||
->toContain('padding-inline: 15px;')
|
->toContain('padding-inline: 15px;')
|
||||||
->toContain('padding-inline-start: 7px;')
|
->toContain('padding-inline-start: 7px;')
|
||||||
->and((string) file_get_contents(__DIR__.'/../../../resources/css/components.css'))->toContain("@import './components/chip.css';");
|
->and((string) file_get_contents(__DIR__.'/../../../resources/css/all.css'))->toContain("@import './components/chip.css';");
|
||||||
});
|
});
|
||||||
|
|
||||||
it('pads a chip without icons by 16px on both sides', function () {
|
it('pads a chip without icons by 16px on both sides', function () {
|
||||||
@@ -245,7 +245,7 @@ it('draws the set from its stylesheet: 8px between chips, wrapping unless it scr
|
|||||||
expect($css)->toContain("@import './chip.css';")
|
expect($css)->toContain("@import './chip.css';")
|
||||||
->toMatch('/\[data-md-chip-set-row\] \{\s*display: flex;\s*flex-wrap: wrap;\s*gap: var\(--md-sys-measurement-space100\);/')
|
->toMatch('/\[data-md-chip-set-row\] \{\s*display: flex;\s*flex-wrap: wrap;\s*gap: var\(--md-sys-measurement-space100\);/')
|
||||||
->toContain('[data-md-chip-set-scroller] > [data-md-chip-set-row] {')
|
->toContain('[data-md-chip-set-scroller] > [data-md-chip-set-row] {')
|
||||||
->and((string) file_get_contents(__DIR__.'/../../../resources/css/components.css'))->toContain("@import './components/chip-set.css';");
|
->and((string) file_get_contents(__DIR__.'/../../../resources/css/all.css'))->toContain("@import './components/chip-set.css';");
|
||||||
});
|
});
|
||||||
|
|
||||||
it('scrolls a chip set on one line with fading edges', function () {
|
it('scrolls a chip set on one line with fading edges', function () {
|
||||||
|
|||||||
@@ -106,5 +106,5 @@ it('brings the stylesheets of what it renders, and draws its empty row', functio
|
|||||||
->toContain("@import './chip-set.css';")
|
->toContain("@import './chip-set.css';")
|
||||||
->toContain("@import './chip.css';")
|
->toContain("@import './chip.css';")
|
||||||
->toContain('[data-md-choices-empty] {')
|
->toContain('[data-md-choices-empty] {')
|
||||||
->and((string) file_get_contents(__DIR__.'/../../../resources/css/components.css'))->toContain("@import './components/choices.css';");
|
->and((string) file_get_contents(__DIR__.'/../../../resources/css/all.css'))->toContain("@import './components/choices.css';");
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ use NoNameWeb\LivewireMaterial\Tests\Support\ViewClasses;
|
|||||||
* `data-md-*` attributes and no class list of its own beyond the interaction and text classes
|
* `data-md-*` attributes and no class list of its own beyond the interaction and text classes
|
||||||
* (tests/Support/ViewClasses.php), and each has a stylesheet in `material.components` that imports
|
* (tests/Support/ViewClasses.php), and each has a stylesheet in `material.components` that imports
|
||||||
* the stylesheets of the components its view renders, writes its values from the tokens and its
|
* the stylesheets of the components its view renders, writes its values from the tokens and its
|
||||||
* breakpoints as px range queries, and is imported from the "Containment" block of components.css.
|
* breakpoints as px range queries, and is imported from the "Containment" block of all.css.
|
||||||
*
|
*
|
||||||
* The dataset grew by one name per component commit, the same rule InputStylesheetsTest.php and
|
* The dataset grew by one name per component commit, the same rule InputStylesheetsTest.php and
|
||||||
* ActionStylesheetsTest.php follow. The error layout, whose view is not a component, has the same
|
* ActionStylesheetsTest.php follow. The error layout, whose view is not a component, has the same
|
||||||
@@ -108,12 +108,8 @@ it('takes its values from the tokens and its breakpoints in px', function (strin
|
|||||||
assertContainmentTokensAndPxBreakpoints(ComponentStylesheet::read($name), "{$name}.css");
|
assertContainmentTokensAndPxBreakpoints(ComponentStylesheet::read($name), "{$name}.css");
|
||||||
})->with('containment components');
|
})->with('containment components');
|
||||||
|
|
||||||
it('is imported from the containment block of components.css', function (string $name) {
|
it('is imported from the containment block of all.css', function (string $name) {
|
||||||
$components = File::get(__DIR__.'/../../../resources/css/components.css');
|
expect(allCssBlock('Containment'))->toContain("@import './components/{$name}.css';");
|
||||||
$block = substr($components, (int) strpos($components, '/* Containment */'));
|
|
||||||
$block = substr($block, 0, (int) strpos($block, '/* Navigation */'));
|
|
||||||
|
|
||||||
expect($block)->toContain("@import './components/{$name}.css';");
|
|
||||||
})->with('containment components');
|
})->with('containment components');
|
||||||
|
|
||||||
it('scopes every element-wide selector to a data-md hook', function (string $name) {
|
it('scopes every element-wide selector to a data-md hook', function (string $name) {
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ it('draws 52px rows, 36px dense ones, from its stylesheet in the components laye
|
|||||||
->toMatch('/\\[data-md-table\\] \\{\\s*--cell-x: 12px;\\s*--cell-y: var\\(--md-sys-measurement-space200\\);/')
|
->toMatch('/\\[data-md-table\\] \\{\\s*--cell-x: 12px;\\s*--cell-y: var\\(--md-sys-measurement-space200\\);/')
|
||||||
->toMatch('/\\[data-md-table\\]\\[data-md-dense\\] \\{\\s*--cell-y: var\\(--md-sys-measurement-space100\\);/')
|
->toMatch('/\\[data-md-table\\]\\[data-md-dense\\] \\{\\s*--cell-y: var\\(--md-sys-measurement-space100\\);/')
|
||||||
->toContain('[data-md-table] :where(tbody tr[aria-selected=\'true\'])')
|
->toContain('[data-md-table] :where(tbody tr[aria-selected=\'true\'])')
|
||||||
->and((string) file_get_contents(__DIR__.'/../../../resources/css/components.css'))->toContain("@import './components/table.css';")
|
->and((string) file_get_contents(__DIR__.'/../../../resources/css/all.css'))->toContain("@import './components/table.css';")
|
||||||
->and((string) file_get_contents(__DIR__.'/../../../resources/css/tailwind.css'))->not->toContain('table.css');
|
->and((string) file_get_contents(__DIR__.'/../../../resources/css/tailwind.css'))->not->toContain('table.css');
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -112,7 +112,7 @@ it('draws the sort header from its stylesheet, its arrow hidden until the column
|
|||||||
expect((string) file_get_contents(__DIR__.'/../../../resources/css/components/sort-header.css'))
|
expect((string) file_get_contents(__DIR__.'/../../../resources/css/components/sort-header.css'))
|
||||||
->toContain('@layer material.components')
|
->toContain('@layer material.components')
|
||||||
->toMatch('/\\[data-md-sort-header\\]:not\\(\\[data-md-active\\]\\) \\[data-md-sort-header-arrow\\] \\{\\s*opacity: 0;/')
|
->toMatch('/\\[data-md-sort-header\\]:not\\(\\[data-md-active\\]\\) \\[data-md-sort-header-arrow\\] \\{\\s*opacity: 0;/')
|
||||||
->and((string) file_get_contents(__DIR__.'/../../../resources/css/components.css'))->toContain("@import './components/sort-header.css';");
|
->and((string) file_get_contents(__DIR__.'/../../../resources/css/all.css'))->toContain("@import './components/sort-header.css';");
|
||||||
});
|
});
|
||||||
|
|
||||||
it('draws the paginators from their stylesheet, trading the numbers for "Page n of m" below 600px', function () {
|
it('draws the paginators from their stylesheet, trading the numbers for "Page n of m" below 600px', function () {
|
||||||
@@ -120,5 +120,5 @@ it('draws the paginators from their stylesheet, trading the numbers for "Page n
|
|||||||
->toContain('@layer material.components')
|
->toContain('@layer material.components')
|
||||||
->toMatch('/@media \\(width < 600px\\) \\{\\s*\\[data-md-pagination-page\\],\\s*\\[data-md-pagination-range\\] \\{\\s*display: none;/')
|
->toMatch('/@media \\(width < 600px\\) \\{\\s*\\[data-md-pagination-page\\],\\s*\\[data-md-pagination-range\\] \\{\\s*display: none;/')
|
||||||
->toMatch('/@media \\(width >= 600px\\) \\{\\s*\\[data-md-pagination-compact\\] \\{\\s*display: none;/')
|
->toMatch('/@media \\(width >= 600px\\) \\{\\s*\\[data-md-pagination-compact\\] \\{\\s*display: none;/')
|
||||||
->and((string) file_get_contents(__DIR__.'/../../../resources/css/components.css'))->toContain("@import './components/pagination.css';");
|
->and((string) file_get_contents(__DIR__.'/../../../resources/css/all.css'))->toContain("@import './components/pagination.css';");
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -38,5 +38,5 @@ it('keeps one column on a compact window and fills columns from medium, in the l
|
|||||||
->toContain("[data-md-feed] {\n display: grid;\n grid-template-columns: minmax(0, 1fr);")
|
->toContain("[data-md-feed] {\n display: grid;\n grid-template-columns: minmax(0, 1fr);")
|
||||||
->toContain("@media (width >= 600px) {\n grid-template-columns: repeat(auto-fill, minmax(min(100%, var(--md-min-item, 240px)), 1fr));")
|
->toContain("@media (width >= 600px) {\n grid-template-columns: repeat(auto-fill, minmax(min(100%, var(--md-min-item, 240px)), 1fr));")
|
||||||
->not->toContain('grid-auto-flow')
|
->not->toContain('grid-auto-flow')
|
||||||
->and((string) file_get_contents(__DIR__.'/../../../resources/css/layout.css'))->toContain("@import './layout/feed.css';");
|
->and((string) file_get_contents(__DIR__.'/../../../resources/css/all.css'))->toContain("@import './layout/feed.css';");
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -225,7 +225,7 @@ it('draws a form\'s column and its actions from its stylesheet', function () {
|
|||||||
expect($css)->toContain('@layer material.components')
|
expect($css)->toContain('@layer material.components')
|
||||||
->toContain('grid-template-columns: minmax(0, 1fr);')
|
->toContain('grid-template-columns: minmax(0, 1fr);')
|
||||||
->toContain('gap: var(--md-sys-measurement-space200);')
|
->toContain('gap: var(--md-sys-measurement-space200);')
|
||||||
->and((string) file_get_contents(__DIR__.'/../../../resources/css/components.css'))->toContain("@import './components/form.css';");
|
->and((string) file_get_contents(__DIR__.'/../../../resources/css/all.css'))->toContain("@import './components/form.css';");
|
||||||
});
|
});
|
||||||
|
|
||||||
it('keeps the field\'s chrome in its own stylesheet in the components layer', function () {
|
it('keeps the field\'s chrome in its own stylesheet in the components layer', function () {
|
||||||
@@ -235,7 +235,7 @@ it('keeps the field\'s chrome in its own stylesheet in the components layer', fu
|
|||||||
->toContain("@import './icon.css';")
|
->toContain("@import './icon.css';")
|
||||||
->toContain('[data-md-field-box]')
|
->toContain('[data-md-field-box]')
|
||||||
->not->toMatch('/(?<![\\w-])\\.field\\b/')
|
->not->toMatch('/(?<![\\w-])\\.field\\b/')
|
||||||
->and((string) file_get_contents(__DIR__.'/../../../resources/css/components.css'))->toContain("@import './components/field.css';")
|
->and((string) file_get_contents(__DIR__.'/../../../resources/css/all.css'))->toContain("@import './components/field.css';")
|
||||||
->and((string) file_get_contents(__DIR__.'/../../../resources/css/tailwind.css'))->not->toContain('field.css');
|
->and((string) file_get_contents(__DIR__.'/../../../resources/css/tailwind.css'))->not->toContain('field.css');
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -258,14 +258,14 @@ it('gives the one-line field a stylesheet that brings the field and its icons',
|
|||||||
expect($css)->toStartWith('/*')
|
expect($css)->toStartWith('/*')
|
||||||
->toContain("@import './field.css';")
|
->toContain("@import './field.css';")
|
||||||
->toContain("@import './icon.css';")
|
->toContain("@import './icon.css';")
|
||||||
->and((string) file_get_contents(__DIR__.'/../../../resources/css/components.css'))->toContain("@import './components/input.css';");
|
->and((string) file_get_contents(__DIR__.'/../../../resources/css/all.css'))->toContain("@import './components/input.css';");
|
||||||
});
|
});
|
||||||
|
|
||||||
it('gives the password field a stylesheet that brings the field and its icons', function () {
|
it('gives the password field a stylesheet that brings the field and its icons', function () {
|
||||||
expect((string) file_get_contents(__DIR__.'/../../../resources/css/components/password.css'))
|
expect((string) file_get_contents(__DIR__.'/../../../resources/css/components/password.css'))
|
||||||
->toContain("@import './field.css';")
|
->toContain("@import './field.css';")
|
||||||
->toContain("@import './icon.css';")
|
->toContain("@import './icon.css';")
|
||||||
->and((string) file_get_contents(__DIR__.'/../../../resources/css/components.css'))->toContain("@import './components/password.css';");
|
->and((string) file_get_contents(__DIR__.'/../../../resources/css/all.css'))->toContain("@import './components/password.css';");
|
||||||
});
|
});
|
||||||
|
|
||||||
it('draws what a textarea changes in the field from its own stylesheet', function () {
|
it('draws what a textarea changes in the field from its own stylesheet', function () {
|
||||||
@@ -276,7 +276,7 @@ it('draws what a textarea changes in the field from its own stylesheet', functio
|
|||||||
->toContain('textarea[data-md-field-control][data-md-autogrow]')
|
->toContain('textarea[data-md-field-control][data-md-autogrow]')
|
||||||
->toContain('field-sizing: content;')
|
->toContain('field-sizing: content;')
|
||||||
->and((string) file_get_contents(__DIR__.'/../../../resources/css/components/field.css'))->not->toContain('textarea[')
|
->and((string) file_get_contents(__DIR__.'/../../../resources/css/components/field.css'))->not->toContain('textarea[')
|
||||||
->and((string) file_get_contents(__DIR__.'/../../../resources/css/components.css'))->toContain("@import './components/textarea.css';");
|
->and((string) file_get_contents(__DIR__.'/../../../resources/css/all.css'))->toContain("@import './components/textarea.css';");
|
||||||
});
|
});
|
||||||
|
|
||||||
it('draws what a select changes in the field from its own stylesheet', function () {
|
it('draws what a select changes in the field from its own stylesheet', function () {
|
||||||
@@ -286,7 +286,7 @@ it('draws what a select changes in the field from its own stylesheet', function
|
|||||||
->toContain('@supports (appearance: base-select)')
|
->toContain('@supports (appearance: base-select)')
|
||||||
->toContain('select[data-md-field-control]:open')
|
->toContain('select[data-md-field-control]:open')
|
||||||
->and((string) file_get_contents(__DIR__.'/../../../resources/css/components/field.css'))->not->toContain('select[')
|
->and((string) file_get_contents(__DIR__.'/../../../resources/css/components/field.css'))->not->toContain('select[')
|
||||||
->and((string) file_get_contents(__DIR__.'/../../../resources/css/components.css'))->toContain("@import './components/select.css';");
|
->and((string) file_get_contents(__DIR__.'/../../../resources/css/all.css'))->toContain("@import './components/select.css';");
|
||||||
});
|
});
|
||||||
|
|
||||||
it('draws the file picker\'s button from its own stylesheet', function () {
|
it('draws the file picker\'s button from its own stylesheet', function () {
|
||||||
@@ -296,5 +296,5 @@ it('draws the file picker\'s button from its own stylesheet', function () {
|
|||||||
->toContain("input[type='file'][data-md-field-control]::file-selector-button")
|
->toContain("input[type='file'][data-md-field-control]::file-selector-button")
|
||||||
->toContain('background-color: var(--md-sys-color-secondary-container);')
|
->toContain('background-color: var(--md-sys-color-secondary-container);')
|
||||||
->and((string) file_get_contents(__DIR__.'/../../../resources/css/components/field.css'))->not->toContain("[type='file']")
|
->and((string) file_get_contents(__DIR__.'/../../../resources/css/components/field.css'))->not->toContain("[type='file']")
|
||||||
->and((string) file_get_contents(__DIR__.'/../../../resources/css/components.css'))->toContain("@import './components/file.css';");
|
->and((string) file_get_contents(__DIR__.'/../../../resources/css/all.css'))->toContain("@import './components/file.css';");
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -75,5 +75,5 @@ it('reads its own column count at each breakpoint in the layout layer', function
|
|||||||
expect($css)->toContain("@media (width >= {$width}px) {\n --md-columns: var(--md-columns-{$breakpoint}, 1);");
|
expect($css)->toContain("@media (width >= {$width}px) {\n --md-columns: var(--md-columns-{$breakpoint}, 1);");
|
||||||
}
|
}
|
||||||
|
|
||||||
expect((string) file_get_contents(__DIR__.'/../../../resources/css/layout.css'))->toContain("@import './layout/grid.css';");
|
expect((string) file_get_contents(__DIR__.'/../../../resources/css/all.css'))->toContain("@import './layout/grid.css';");
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ it('draws its size from a stylesheet in the components layer', function () {
|
|||||||
expect($css)->toContain('@layer material.components')
|
expect($css)->toContain('@layer material.components')
|
||||||
->toContain('inline-size: var(--md-icon-size, 24px)')
|
->toContain('inline-size: var(--md-icon-size, 24px)')
|
||||||
->toContain('[data-md-icon][data-md-mirror-rtl]:dir(rtl)')
|
->toContain('[data-md-icon][data-md-mirror-rtl]:dir(rtl)')
|
||||||
->and((string) file_get_contents(__DIR__.'/../../../resources/css/components.css'))->toContain("@import './components/icon.css';");
|
->and((string) file_get_contents(__DIR__.'/../../../resources/css/all.css'))->toContain("@import './components/icon.css';");
|
||||||
});
|
});
|
||||||
|
|
||||||
it('names the icon for a screen reader when it carries the meaning alone', function () {
|
it('names the icon for a screen reader when it carries the meaning alone', function () {
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ use NoNameWeb\LivewireMaterial\Tests\Support\ViewClasses;
|
|||||||
* classes (tests/Support/ViewClasses.php), and each has a stylesheet in
|
* classes (tests/Support/ViewClasses.php), and each has a stylesheet in
|
||||||
* `material.components` that imports the stylesheets of the components its view renders, writes
|
* `material.components` that imports the stylesheets of the components its view renders, writes
|
||||||
* its values from the tokens and its breakpoints as px range queries, and is imported from the
|
* its values from the tokens and its breakpoints as px range queries, and is imported from the
|
||||||
* "Inputs, selection and data" block of components.css.
|
* "Inputs, selection and data" block of all.css.
|
||||||
*
|
*
|
||||||
* `pagination` is not in this dataset: its four views live in `resources/views/pagination/**`, not
|
* `pagination` is not in this dataset: its four views live in `resources/views/pagination/**`, not
|
||||||
* `resources/views/components/`, so the two checks that read a single `resources/views/components/
|
* `resources/views/components/`, so the two checks that read a single `resources/views/components/
|
||||||
@@ -47,10 +47,9 @@ dataset('input components', inputComponents());
|
|||||||
/**
|
/**
|
||||||
* The checks that read a stylesheet alone also run on the one no view renders by itself:
|
* The checks that read a stylesheet alone also run on the one no view renders by itself:
|
||||||
* selection.css, what the checkbox, radio and switch share (its own file header names them), which
|
* selection.css, what the checkbox, radio and switch share (its own file header names them), which
|
||||||
* each of their stylesheets imports. Nothing renders it directly and components.css never imports
|
* each of their stylesheets imports too. Nothing renders it directly, so without this its shape
|
||||||
* it either — only checkbox.css, radio.css and toggle.css do — so without this its shape and token
|
* and token rules would go unchecked, the way navigation-item.css did before
|
||||||
* rules would go unchecked, the way navigation-item.css did before NavigationStylesheetsTest.php
|
* NavigationStylesheetsTest.php picked it up.
|
||||||
* picked it up.
|
|
||||||
*/
|
*/
|
||||||
dataset('input stylesheets', [...inputComponents(), 'selection']);
|
dataset('input stylesheets', [...inputComponents(), 'selection']);
|
||||||
|
|
||||||
@@ -155,12 +154,8 @@ it('takes its values from the tokens and its breakpoints in px', function (strin
|
|||||||
assertTokensAndPxBreakpoints(ComponentStylesheet::read($name), "{$name}.css");
|
assertTokensAndPxBreakpoints(ComponentStylesheet::read($name), "{$name}.css");
|
||||||
})->with('input stylesheets');
|
})->with('input stylesheets');
|
||||||
|
|
||||||
it('is imported from the inputs, selection and data block of components.css', function (string $name) {
|
it('is imported from the inputs, selection and data block of all.css', function (string $name) {
|
||||||
$components = File::get(__DIR__.'/../../../resources/css/components.css');
|
expect(allCssBlock('Inputs, selection and data'))->toContain("@import './components/{$name}.css';");
|
||||||
$block = substr($components, (int) strpos($components, '/* Inputs, selection and data */'));
|
|
||||||
$block = substr($block, 0, (int) strpos($block, '/* Containment */'));
|
|
||||||
|
|
||||||
expect($block)->toContain("@import './components/{$name}.css';");
|
|
||||||
})->with('input components');
|
})->with('input components');
|
||||||
|
|
||||||
// ---- pagination: its views live outside resources/views/components/ --------------------------
|
// ---- pagination: its views live outside resources/views/components/ --------------------------
|
||||||
@@ -183,12 +178,8 @@ it('takes pagination\'s values from the tokens and its breakpoints in px', funct
|
|||||||
assertTokensAndPxBreakpoints(ComponentStylesheet::read('pagination'), 'pagination.css');
|
assertTokensAndPxBreakpoints(ComponentStylesheet::read('pagination'), 'pagination.css');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('is imported from the inputs, selection and data block of components.css for pagination', function () {
|
it('is imported from the inputs, selection and data block of all.css for pagination', function () {
|
||||||
$components = File::get(__DIR__.'/../../../resources/css/components.css');
|
expect(allCssBlock('Inputs, selection and data'))->toContain("@import './components/pagination.css';");
|
||||||
$block = substr($components, (int) strpos($components, '/* Inputs, selection and data */'));
|
|
||||||
$block = substr($block, 0, (int) strpos($block, '/* Containment */'));
|
|
||||||
|
|
||||||
expect($block)->toContain("@import './components/pagination.css';");
|
|
||||||
});
|
});
|
||||||
|
|
||||||
dataset('pagination views', [
|
dataset('pagination views', [
|
||||||
|
|||||||
@@ -88,6 +88,6 @@ it('shows one pane below expanded and both from it, at M3\'s fixed pane widths',
|
|||||||
->toContain("@media (width >= 1200px) {\n grid-template-columns: 412px minmax(0, 1fr);")
|
->toContain("@media (width >= 1200px) {\n grid-template-columns: 412px minmax(0, 1fr);")
|
||||||
->toContain("@media (width < 840px) {\n [data-md-list-detail]:not([data-md-selected]) > [data-md-list-detail-pane='detail'],\n [data-md-list-detail][data-md-selected] > [data-md-list-detail-pane='list'] {\n display: none;")
|
->toContain("@media (width < 840px) {\n [data-md-list-detail]:not([data-md-selected]) > [data-md-list-detail-pane='detail'],\n [data-md-list-detail][data-md-selected] > [data-md-list-detail-pane='list'] {\n display: none;")
|
||||||
->toContain('[data-md-list-detail-back] [data-md-icon]:dir(rtl)')
|
->toContain('[data-md-list-detail-back] [data-md-icon]:dir(rtl)')
|
||||||
->and((string) file_get_contents(__DIR__.'/../../../resources/css/layout.css'))->toContain("@import './layout/list-detail.css';")
|
->and((string) file_get_contents(__DIR__.'/../../../resources/css/all.css'))->toContain("@import './layout/list-detail.css';")
|
||||||
->and((string) file_get_contents(__DIR__.'/../../../resources/js/material.js'))->toContain("import './layout.js'");
|
->and((string) file_get_contents(__DIR__.'/../../../resources/js/material.js'))->toContain("import './layout.js'");
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ use NoNameWeb\LivewireMaterial\Tests\Support\ViewClasses;
|
|||||||
* classes (tests/Support/ViewClasses.php), and each has a stylesheet in `material.components` that
|
* classes (tests/Support/ViewClasses.php), and each has a stylesheet in `material.components` that
|
||||||
* imports the stylesheets of the components its view renders, writes its values from the tokens
|
* imports the stylesheets of the components its view renders, writes its values from the tokens
|
||||||
* and its breakpoints as px range queries, and is imported from the "Navigation" block of
|
* and its breakpoints as px range queries, and is imported from the "Navigation" block of
|
||||||
* components.css.
|
* all.css.
|
||||||
*
|
*
|
||||||
* The dataset grows by one name per component commit, the same rule ContainmentStylesheetsTest.php
|
* The dataset grows by one name per component commit, the same rule ContainmentStylesheetsTest.php
|
||||||
* and the earlier groups follow. `<x-tabs>` and `<x-tab>` share one stylesheet (tabs.css) and one
|
* and the earlier groups follow. `<x-tabs>` and `<x-tab>` share one stylesheet (tabs.css) and one
|
||||||
@@ -148,11 +148,8 @@ it('takes its values from the tokens and its breakpoints in px', function (strin
|
|||||||
assertNavigationTokensAndPxBreakpoints(ComponentStylesheet::read($name), "{$name}.css");
|
assertNavigationTokensAndPxBreakpoints(ComponentStylesheet::read($name), "{$name}.css");
|
||||||
})->with('navigation stylesheets');
|
})->with('navigation stylesheets');
|
||||||
|
|
||||||
it('is imported from the navigation block of components.css', function (string $name) {
|
it('is imported from the navigation block of all.css', function (string $name) {
|
||||||
$components = File::get(__DIR__.'/../../../resources/css/components.css');
|
expect(allCssBlock('Navigation'))->toContain("@import './components/{$name}.css';");
|
||||||
$block = substr($components, (int) strpos($components, '/* Navigation */'));
|
|
||||||
|
|
||||||
expect($block)->toContain("@import './components/{$name}.css';");
|
|
||||||
})->with('navigation stylesheets');
|
})->with('navigation stylesheets');
|
||||||
|
|
||||||
it('scopes every element-wide selector to a data-md hook', function (string $name) {
|
it('scopes every element-wide selector to a data-md hook', function (string $name) {
|
||||||
|
|||||||
@@ -100,7 +100,7 @@ it('divides a scrolling body from its header and actions only while content is h
|
|||||||
->toContain('position: absolute;')
|
->toContain('position: absolute;')
|
||||||
// A token that reduced motion sets to zero, so there the rule simply appears.
|
// A token that reduced motion sets to zero, so there the rule simply appears.
|
||||||
->toContain('transition: opacity var(--md-sys-motion-effects-fast-duration) var(--md-sys-motion-effects-fast);')
|
->toContain('transition: opacity var(--md-sys-motion-effects-fast-duration) var(--md-sys-motion-effects-fast);')
|
||||||
->and(File::get(__DIR__.'/../../../resources/css/components.css'))->toContain("@import './components/modal.css';")
|
->and(File::get(__DIR__.'/../../../resources/css/all.css'))->toContain("@import './components/modal.css';")
|
||||||
->and(File::get(__DIR__.'/../../../resources/css/tailwind.css'))->not->toContain('dialog.css')
|
->and(File::get(__DIR__.'/../../../resources/css/tailwind.css'))->not->toContain('dialog.css')
|
||||||
->and(File::get(__DIR__.'/../../../resources/js/material.js'))->toContain("import './dialog.js'")
|
->and(File::get(__DIR__.'/../../../resources/js/material.js'))->toContain("import './dialog.js'")
|
||||||
->and(File::get(__DIR__.'/../../../resources/js/dialog.js'))
|
->and(File::get(__DIR__.'/../../../resources/js/dialog.js'))
|
||||||
@@ -273,7 +273,7 @@ it('draws the drawer from a stylesheet imported from the containment block, with
|
|||||||
->not->toContain('pane-width')
|
->not->toContain('pane-width')
|
||||||
->not->toContain('pane-close-on-escape')
|
->not->toContain('pane-close-on-escape')
|
||||||
->not->toContain('data-pane')
|
->not->toContain('data-pane')
|
||||||
->and(File::get(__DIR__.'/../../../resources/css/components.css'))
|
->and(File::get(__DIR__.'/../../../resources/css/all.css'))
|
||||||
->toContain("@import './components/drawer.css';");
|
->toContain("@import './components/drawer.css';");
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -305,7 +305,7 @@ it('gives a bottom sheet M3\'s preset heights, cycled from the drag handle', fun
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('draws the bottom sheet from a stylesheet imported from the containment block and by the menu', function () {
|
it('draws the bottom sheet from a stylesheet imported from the containment block and by the menu', function () {
|
||||||
expect(File::get(__DIR__.'/../../../resources/css/components.css'))
|
expect(File::get(__DIR__.'/../../../resources/css/all.css'))
|
||||||
->toContain("@import './components/bottom-sheet.css';")
|
->toContain("@import './components/bottom-sheet.css';")
|
||||||
->and(File::get(__DIR__.'/../../../resources/css/components/menu.css'))
|
->and(File::get(__DIR__.'/../../../resources/css/components/menu.css'))
|
||||||
->toContain("@import './bottom-sheet.css';")
|
->toContain("@import './bottom-sheet.css';")
|
||||||
|
|||||||
@@ -87,5 +87,5 @@ it('keeps M3\'s margin on the body, once, in the layout layer', function () {
|
|||||||
->toContain("@media (width >= 600px) {\n padding-inline: var(--md-layout-margin, var(--md-sys-measurement-space300));")
|
->toContain("@media (width >= 600px) {\n padding-inline: var(--md-layout-margin, var(--md-sys-measurement-space300));")
|
||||||
->toContain("[data-md-pane-body] > * {\n --md-layout-margin: 0px;")
|
->toContain("[data-md-pane-body] > * {\n --md-layout-margin: 0px;")
|
||||||
->toContain("[data-md-pane][data-md-width='narrow'] {\n max-inline-size: 40rem;")
|
->toContain("[data-md-pane][data-md-width='narrow'] {\n max-inline-size: 40rem;")
|
||||||
->and((string) file_get_contents(__DIR__.'/../../../resources/css/layout.css'))->toContain("@import './layout/pane.css';");
|
->and((string) file_get_contents(__DIR__.'/../../../resources/css/all.css'))->toContain("@import './layout/pane.css';");
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -56,5 +56,5 @@ it('draws the row from a stylesheet in the layout layer, stacking at the breakpo
|
|||||||
expect($css)->toContain('@layer material.layout')
|
expect($css)->toContain('@layer material.layout')
|
||||||
->toContain("@media (width < 840px) {\n [data-md-row][data-md-stack-below='expanded'] {\n flex-direction: column;")
|
->toContain("@media (width < 840px) {\n [data-md-row][data-md-stack-below='expanded'] {\n flex-direction: column;")
|
||||||
->toContain("[data-md-row][data-md-stack-below='expanded']:not([data-md-align]) {\n align-items: stretch;")
|
->toContain("[data-md-row][data-md-stack-below='expanded']:not([data-md-align]) {\n align-items: stretch;")
|
||||||
->and((string) file_get_contents(__DIR__.'/../../../resources/css/layout.css'))->toContain("@import './layout/row.css';");
|
->and((string) file_get_contents(__DIR__.'/../../../resources/css/all.css'))->toContain("@import './layout/row.css';");
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -268,7 +268,7 @@ it('places the FAB at the bottom-end corner, clear of the bar and of a snackbar,
|
|||||||
// Only the region's own <main>: one an application nests in the page takes none of this.
|
// Only the region's own <main>: one an application nests in the page takes none of this.
|
||||||
->toContain("[data-md-scaffold-content] > main {\n --md-layout-margin: 0px;")
|
->toContain("[data-md-scaffold-content] > main {\n --md-layout-margin: 0px;")
|
||||||
->not->toContain('[data-md-scaffold] main')
|
->not->toContain('[data-md-scaffold] main')
|
||||||
->and((string) file_get_contents(__DIR__.'/../../../resources/css/layout.css'))->toContain("@import './layout/scaffold.css';");
|
->and((string) file_get_contents(__DIR__.'/../../../resources/css/all.css'))->toContain("@import './layout/scaffold.css';");
|
||||||
});
|
});
|
||||||
|
|
||||||
it('imports the stylesheets of the navigation rail, the navigation bar and the snackbar host', function () {
|
it('imports the stylesheets of the navigation rail, the navigation bar and the snackbar host', function () {
|
||||||
|
|||||||
@@ -114,6 +114,6 @@ it('draws the bar and the view from its stylesheet, at M3\'s widths', function (
|
|||||||
->toContain('[data-md-search][data-md-full-screen] [data-md-search-view] {')
|
->toContain('[data-md-search][data-md-full-screen] [data-md-search-view] {')
|
||||||
->toContain("[data-md-search][data-md-trigger='icon']:not([data-md-open]) [data-md-search-bar] {")
|
->toContain("[data-md-search][data-md-trigger='icon']:not([data-md-open]) [data-md-search-bar] {")
|
||||||
->not->toMatch('/\\d(?:\\.\\d+)?rem/')
|
->not->toMatch('/\\d(?:\\.\\d+)?rem/')
|
||||||
->and((string) file_get_contents(__DIR__.'/../../../resources/css/components.css'))->toContain("@import './components/search.css';")
|
->and((string) file_get_contents(__DIR__.'/../../../resources/css/all.css'))->toContain("@import './components/search.css';")
|
||||||
->and((string) file_get_contents(__DIR__.'/../../../resources/css/tailwind.css'))->not->toContain('search.css');
|
->and((string) file_get_contents(__DIR__.'/../../../resources/css/tailwind.css'))->not->toContain('search.css');
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -151,7 +151,7 @@ it('draws the checkbox from its stylesheet, on the row the selection controls sh
|
|||||||
->and((string) file_get_contents(__DIR__.'/../../../resources/css/components/selection.css'))
|
->and((string) file_get_contents(__DIR__.'/../../../resources/css/components/selection.css'))
|
||||||
->toContain('[data-md-selection-row]')
|
->toContain('[data-md-selection-row]')
|
||||||
->toContain('@layer material.components')
|
->toContain('@layer material.components')
|
||||||
->and((string) file_get_contents(__DIR__.'/../../../resources/css/components.css'))->toContain("@import './components/checkbox.css';")
|
->and((string) file_get_contents(__DIR__.'/../../../resources/css/all.css'))->toContain("@import './components/checkbox.css';")
|
||||||
->and((string) file_get_contents(__DIR__.'/../../../resources/css/tailwind.css'))->not->toContain('selection.css');
|
->and((string) file_get_contents(__DIR__.'/../../../resources/css/tailwind.css'))->not->toContain('selection.css');
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -160,7 +160,7 @@ it('lays the radio group out from its stylesheet, inline only from 600px', funct
|
|||||||
|
|
||||||
expect($css)->toContain("@import './selection.css';")
|
expect($css)->toContain("@import './selection.css';")
|
||||||
->toMatch('/@media \\(width >= 600px\\) \\{\\s*\\[data-md-radio\\]\\[data-md-inline\\] \\[data-md-radio-options\\] \\{\\s*display: flex;/')
|
->toMatch('/@media \\(width >= 600px\\) \\{\\s*\\[data-md-radio\\]\\[data-md-inline\\] \\[data-md-radio-options\\] \\{\\s*display: flex;/')
|
||||||
->and((string) file_get_contents(__DIR__.'/../../../resources/css/components.css'))->toContain("@import './components/radio.css';")
|
->and((string) file_get_contents(__DIR__.'/../../../resources/css/all.css'))->toContain("@import './components/radio.css';")
|
||||||
->and((string) $this->blade('<x-radio name="size" class="mt-4" style="order: 1" :options="[[\'id\' => \'s\', \'name\' => \'S\']]" />'))
|
->and((string) $this->blade('<x-radio name="size" class="mt-4" style="order: 1" :options="[[\'id\' => \'s\', \'name\' => \'S\']]" />'))
|
||||||
->toMatch('/data-md-radio\\s+class="mt-4" style="order: 1"/');
|
->toMatch('/data-md-radio\\s+class="mt-4" style="order: 1"/');
|
||||||
});
|
});
|
||||||
@@ -169,5 +169,5 @@ it('draws the switch from its stylesheet', function () {
|
|||||||
expect((string) file_get_contents(__DIR__.'/../../../resources/css/components/toggle.css'))
|
expect((string) file_get_contents(__DIR__.'/../../../resources/css/components/toggle.css'))
|
||||||
->toContain("@import './selection.css';")
|
->toContain("@import './selection.css';")
|
||||||
->toMatch('/\\[data-md-switch\\] \\{[^}]*width: 52px;[^}]*height: 32px;/')
|
->toMatch('/\\[data-md-switch\\] \\{[^}]*width: 52px;[^}]*height: 32px;/')
|
||||||
->and((string) file_get_contents(__DIR__.'/../../../resources/css/components.css'))->toContain("@import './components/toggle.css';");
|
->and((string) file_get_contents(__DIR__.'/../../../resources/css/all.css'))->toContain("@import './components/toggle.css';");
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ it('draws M3\'s 44x48 value indicator and puts the stop on the inactive track',
|
|||||||
->toMatch('/\[data-md-slider-value\] \{[^}]*min-width: var\(--md-sys-measurement-space600\);[^}]*height: 44px;[^}]*background-color: var\(--md-sys-color-inverse-surface\);[^}]*transform-origin: bottom;/')
|
->toMatch('/\[data-md-slider-value\] \{[^}]*min-width: var\(--md-sys-measurement-space600\);[^}]*height: 44px;[^}]*background-color: var\(--md-sys-color-inverse-surface\);[^}]*transform-origin: bottom;/')
|
||||||
->toMatch('/:is\(\[data-md-slider-tick\], \[data-md-slider-stop\]\) \{[^}]*background-color: var\(--slider-on-inactive\);/')
|
->toMatch('/:is\(\[data-md-slider-tick\], \[data-md-slider-stop\]\) \{[^}]*background-color: var\(--slider-on-inactive\);/')
|
||||||
->toContain("@import './icon.css';")
|
->toContain("@import './icon.css';")
|
||||||
->and((string) file_get_contents(__DIR__.'/../../../resources/css/components.css'))->toContain("@import './components/slider.css';");
|
->and((string) file_get_contents(__DIR__.'/../../../resources/css/all.css'))->toContain("@import './components/slider.css';");
|
||||||
});
|
});
|
||||||
|
|
||||||
it('snaps and clamps the value to the step grid', function (string $template, string $value) {
|
it('snaps and clamps the value to the step grid', function (string $template, string $value) {
|
||||||
|
|||||||
@@ -32,12 +32,12 @@ it('takes the element, the visibility props and the caller\'s class and style',
|
|||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('draws the stack from a stylesheet in the layout layer, imported by layout.css', function () {
|
it('draws the stack from a stylesheet in the layout layer, imported by all.css', function () {
|
||||||
$css = (string) file_get_contents(__DIR__.'/../../../resources/css/layout/stack.css');
|
$css = (string) file_get_contents(__DIR__.'/../../../resources/css/layout/stack.css');
|
||||||
|
|
||||||
expect($css)->toContain('@layer material.layout')
|
expect($css)->toContain('@layer material.layout')
|
||||||
->toContain("[data-md-stack] {\n display: flex;\n flex-direction: column;\n gap: var(--md-gap);")
|
->toContain("[data-md-stack] {\n display: flex;\n flex-direction: column;\n gap: var(--md-gap);")
|
||||||
// A stack inside another never takes its parent's gap.
|
// A stack inside another never takes its parent's gap.
|
||||||
->toContain(":where([data-md-stack]) {\n --md-gap: 0px;")
|
->toContain(":where([data-md-stack]) {\n --md-gap: 0px;")
|
||||||
->and((string) file_get_contents(__DIR__.'/../../../resources/css/layout.css'))->toContain("@import './layout/stack.css';");
|
->and((string) file_get_contents(__DIR__.'/../../../resources/css/all.css'))->toContain("@import './layout/stack.css';");
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -68,5 +68,5 @@ it('places the supporting pane at M3\'s widths from expanded, in the layout laye
|
|||||||
->toContain("@media (width >= 1200px) {\n grid-template-columns: minmax(0, 1fr) 412px;")
|
->toContain("@media (width >= 1200px) {\n grid-template-columns: minmax(0, 1fr) 412px;")
|
||||||
->toContain("[data-md-supporting-pane][data-md-width='split'] {\n grid-template-columns: minmax(0, 2fr) minmax(0, 1fr);")
|
->toContain("[data-md-supporting-pane][data-md-width='split'] {\n grid-template-columns: minmax(0, 2fr) minmax(0, 1fr);")
|
||||||
->toContain('inset-block-end: max(var(--material-bottom-bar, 0px), var(--material-safe-bottom, env(safe-area-inset-bottom)));')
|
->toContain('inset-block-end: max(var(--material-bottom-bar, 0px), var(--material-safe-bottom, env(safe-area-inset-bottom)));')
|
||||||
->and((string) file_get_contents(__DIR__.'/../../../resources/css/layout.css'))->toContain("@import './layout/supporting-pane.css';");
|
->and((string) file_get_contents(__DIR__.'/../../../resources/css/all.css'))->toContain("@import './layout/supporting-pane.css';");
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ it('puts the caller\'s class and style on the surface untouched', function () {
|
|||||||
->toMatchArray(['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 layout.css', function () {
|
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');
|
$css = (string) file_get_contents(__DIR__.'/../../../resources/css/layout/surface.css');
|
||||||
|
|
||||||
expect($css)->toContain('@layer material.layout')
|
expect($css)->toContain('@layer material.layout')
|
||||||
@@ -69,5 +69,5 @@ it('draws the surface from a stylesheet in the layout layer, imported by layout.
|
|||||||
->toContain("@import './visibility.css';")
|
->toContain("@import './visibility.css';")
|
||||||
// A pane on a surface keeps its content off the surface's edge again.
|
// A pane on a surface keeps its content off the surface's edge again.
|
||||||
->toContain("[data-md-surface] > * {\n --md-layout-margin: initial;")
|
->toContain("[data-md-surface] > * {\n --md-layout-margin: initial;")
|
||||||
->and((string) file_get_contents(__DIR__.'/../../../resources/css/layout.css'))->toContain("@import './layout/surface.css';");
|
->and((string) file_get_contents(__DIR__.'/../../../resources/css/all.css'))->toContain("@import './layout/surface.css';");
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -273,12 +273,8 @@ it('takes its values from the tokens and its breakpoints in px', function () {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
it('is imported from the containment block of components.css', function () {
|
it('is imported from the containment block of all.css', function () {
|
||||||
$components = File::get(__DIR__.'/../../resources/css/components.css');
|
expect(allCssBlock('Containment'))->toContain("@import './components/error-page.css';");
|
||||||
$block = substr($components, (int) strpos($components, '/* Containment */'));
|
|
||||||
$block = substr($block, 0, (int) strpos($block, '/* Navigation */'));
|
|
||||||
|
|
||||||
expect($block)->toContain("@import './components/error-page.css';");
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('styles the body only when it holds the error layout, and turns the shape on both paths', function () {
|
it('styles the body only when it holds the error layout, and turns the shape on both paths', function () {
|
||||||
|
|||||||
@@ -10,8 +10,14 @@ use NoNameWeb\LivewireMaterial\Support\Layout;
|
|||||||
* would nest the layer twice); then its own `@layer material.<x>` blocks. The only rules outside
|
* would nest the layer twice); then its own `@layer material.<x>` blocks. The only rules outside
|
||||||
* a layer are foundation/hidden.css's two `!important` ones.
|
* a layer are foundation/hidden.css's two `!important` ones.
|
||||||
*
|
*
|
||||||
* Until the components leave Tailwind, the checks cover the foundation — foundation.css and every
|
* Every package component and layout stylesheet is plain CSS now (plan step 36), so the shape,
|
||||||
* file it imports, text.css included — and material.css and tailwind.css carry the rest.
|
* Tailwind-free and breakpoint checks below run over the whole tree `all.css` reaches (foundation,
|
||||||
|
* layout and components together) rather than the foundation alone — everything but
|
||||||
|
* `tailwind.css` and `tokens/theme.css`/`tokens/utilities.css`, which stay Tailwind until plan
|
||||||
|
* step 39 and are simply never reached from `all.css`. A component's own values (colours as
|
||||||
|
* roles, shadows as elevation levels, and so on) are each group's own concern, in
|
||||||
|
* tests/Feature/Components/*StylesheetsTest.php; `bundle()`, the PHP bundler `all.css` is built
|
||||||
|
* for, is tests/Feature/StylesheetsBundleTest.php's.
|
||||||
*/
|
*/
|
||||||
const MATERIAL_LAYER_STATEMENT = '@layer material.reset, material.tokens, material.base, material.layout, material.components, material.text, material.visibility;';
|
const MATERIAL_LAYER_STATEMENT = '@layer material.reset, material.tokens, material.base, material.layout, material.components, material.text, material.visibility;';
|
||||||
|
|
||||||
@@ -170,6 +176,19 @@ function foundationStylesheets(): array
|
|||||||
return stylesheetTree(stylesheetPath('foundation.css'));
|
return stylesheetTree(stylesheetPath('foundation.css'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Everything all.css reaches: the foundation, every layout stylesheet and every component
|
||||||
|
* stylesheet, each once. What a check runs over now that every one of them is plain CSS (plan
|
||||||
|
* step 36) — `tailwind.css` and its two token files are never in this tree, since all.css does
|
||||||
|
* not import them.
|
||||||
|
*
|
||||||
|
* @return list<string>
|
||||||
|
*/
|
||||||
|
function allStylesheets(): array
|
||||||
|
{
|
||||||
|
return stylesheetTree(stylesheetPath('all.css'));
|
||||||
|
}
|
||||||
|
|
||||||
function stylesheetName(string $file): string
|
function stylesheetName(string $file): string
|
||||||
{
|
{
|
||||||
return substr($file, strlen(stylesheetPath()) + 1);
|
return substr($file, strlen(stylesheetPath()) + 1);
|
||||||
@@ -189,12 +208,12 @@ it('brings every foundation file and text.css into the foundation, and nothing o
|
|||||||
->and($foundation)
|
->and($foundation)
|
||||||
->toContain('tokens/scheme.css', 'tokens/shape.css', 'tokens/elevation.css', 'tokens/motion.css', 'tokens/type.css', 'tokens/state.css', 'tokens/spacing.css', 'tokens/font.css')
|
->toContain('tokens/scheme.css', 'tokens/shape.css', 'tokens/elevation.css', 'tokens/motion.css', 'tokens/type.css', 'tokens/state.css', 'tokens/spacing.css', 'tokens/font.css')
|
||||||
// The Tailwind half: the theme, the token utilities and the components still written for it.
|
// The Tailwind half: the theme, the token utilities and the components still written for it.
|
||||||
->and(array_values(array_intersect($foundation, ['tokens/theme.css', 'tokens/utilities.css', 'tailwind.css', 'material.css'])))->toBe([])
|
->and(array_values(array_intersect($foundation, ['tokens/theme.css', 'tokens/utilities.css', 'tailwind.css'])))->toBe([])
|
||||||
->and(array_values(preg_grep('/^components\//', $foundation)))->toBe([]);
|
->and(array_values(preg_grep('/^components\//', $foundation)))->toBe([]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('opens every foundation stylesheet with a header, the layer statement and plain imports', function () {
|
it('opens every stylesheet all.css reaches with a header, the layer statement and plain imports', function () {
|
||||||
foreach (foundationStylesheets() as $file) {
|
foreach (allStylesheets() as $file) {
|
||||||
$name = stylesheetName($file);
|
$name = stylesheetName($file);
|
||||||
$items = stylesheetItems(File::get($file));
|
$items = stylesheetItems(File::get($file));
|
||||||
|
|
||||||
@@ -261,7 +280,7 @@ it('keeps every foundation rule inside its material layer, but the two that hide
|
|||||||
])->and($important)->toBe(2);
|
])->and($important)->toBe(2);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('defines x-cloak once, where both the foundation and material.css reach it', function () {
|
it('defines x-cloak once, reached from both the foundation and all.css', function () {
|
||||||
$definitions = collect(File::allFiles(stylesheetPath()))
|
$definitions = collect(File::allFiles(stylesheetPath()))
|
||||||
->filter(fn (SplFileInfo $file): bool => $file->getExtension() === 'css')
|
->filter(fn (SplFileInfo $file): bool => $file->getExtension() === 'css')
|
||||||
->filter(fn (SplFileInfo $file): bool => str_contains(stylesheetWithoutComments($file->getContents()), '[x-cloak]'))
|
->filter(fn (SplFileInfo $file): bool => str_contains(stylesheetWithoutComments($file->getContents()), '[x-cloak]'))
|
||||||
@@ -269,40 +288,50 @@ it('defines x-cloak once, where both the foundation and material.css reach it',
|
|||||||
->values()
|
->values()
|
||||||
->all();
|
->all();
|
||||||
|
|
||||||
$material = array_map(stylesheetName(...), stylesheetTree(stylesheetPath('material.css')));
|
|
||||||
|
|
||||||
expect($definitions)->toBe(['foundation/hidden.css'])
|
expect($definitions)->toBe(['foundation/hidden.css'])
|
||||||
->and($material)->toContain('foundation/hidden.css', 'tailwind.css')
|
->and(foundationStylesheets())->toContain(stylesheetPath('foundation/hidden.css'))
|
||||||
// Imported after Tailwind, the reset would sit in a layer above Tailwind's utilities.
|
->and(allStylesheets())->toContain(stylesheetPath('foundation/hidden.css'));
|
||||||
->and(array_values(array_intersect($material, ['foundation.css', 'foundation/reset.css', 'foundation/base.css', 'foundation/interaction.css', 'text.css'])))->toBe([]);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('writes no Tailwind directive in the foundation', function () {
|
it('writes no Tailwind directive anywhere all.css reaches', function () {
|
||||||
foreach (foundationStylesheets() as $file) {
|
foreach (allStylesheets() as $file) {
|
||||||
expect(stylesheetWithoutComments(File::get($file)))
|
expect(stylesheetWithoutComments(File::get($file)))
|
||||||
->not->toMatch('/@(?:tailwind|theme|utility|variant|custom-variant|apply|source|config|plugin|reference)\b/', stylesheetName($file))
|
->not->toMatch('/@(?:tailwind|theme|utility|variant|custom-variant|apply|source|config|plugin|reference)\b/', stylesheetName($file))
|
||||||
->not->toMatch('/--(?:theme|spacing|alpha)\(|\btheme\(|[\'"]tailwindcss[\'"]/', stylesheetName($file));
|
->not->toMatch('/--(?:theme|spacing|alpha)\(|\btheme\(|[\'"]tailwindcss[\'"]/', stylesheetName($file));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
it('writes a media query in the foundation only at M3\'s breakpoints, in px', function () {
|
it('writes a media query, anywhere all.css reaches, only at M3\'s breakpoints, in px', function () {
|
||||||
$queries = collect(foundationStylesheets())
|
$queries = collect(allStylesheets())
|
||||||
->flatMap(function (string $file): array {
|
->flatMap(function (string $file): array {
|
||||||
preg_match_all('/@media\b([^{]*)\{/', stylesheetWithoutComments(File::get($file)), $matches);
|
preg_match_all('/@media\b([^{]*)\{/', stylesheetWithoutComments(File::get($file)), $matches);
|
||||||
|
|
||||||
return array_map(fn (string $query): string => stylesheetName($file).': '.trim($query), $matches[1]);
|
return array_map(fn (string $query): string => stylesheetName($file).': '.trim($query), $matches[1]);
|
||||||
});
|
});
|
||||||
|
|
||||||
// The foundation's own queries (hover, reduced motion) hold no width; the check is for what
|
// Some queries (hover, reduced motion) hold no width at all; the check below is only for the
|
||||||
// lands here later.
|
// ones that do.
|
||||||
expect($queries)->not->toBeEmpty();
|
expect($queries)->not->toBeEmpty();
|
||||||
|
|
||||||
foreach ($queries as $query) {
|
foreach ($queries as $query) {
|
||||||
preg_match_all('/(\d*\.?\d+)([a-z%]*)/i', substr($query, strpos($query, ': ') + 2), $lengths, PREG_SET_ORDER);
|
$feature = substr($query, strpos($query, ': ') + 2);
|
||||||
|
|
||||||
|
// The one named exception (docs/reference/m3/components-navigation-selection-inputs.md §
|
||||||
|
// Time pickers/Behaviour, and tests/Feature/Components/InputStylesheetsTest.php's
|
||||||
|
// assertBreakpointsInPx()): a viewport *height* in an `orientation` query is not a
|
||||||
|
// breakpoint, since M3 does not make one of it. A width, in any query, still is.
|
||||||
|
$orientationHeight = preg_match('/\(\s*orientation\s*:/', $feature) === 1
|
||||||
|
&& preg_match('/(?:^|[\s:<>=(])(?:min-|max-)?height\b/', $feature) === 1
|
||||||
|
&& ! str_contains($feature, 'width');
|
||||||
|
|
||||||
|
preg_match_all('/(\d*\.?\d+)([a-z%]*)/i', $feature, $lengths, PREG_SET_ORDER);
|
||||||
|
|
||||||
foreach ($lengths as [, $number, $unit]) {
|
foreach ($lengths as [, $number, $unit]) {
|
||||||
expect($unit)->toBe('px', $query)
|
expect($unit)->toBe('px', $query);
|
||||||
->and(in_array($number, ['600', '840', '1200', '1600'], true))->toBeTrue("{$query} is not at an M3 breakpoint");
|
|
||||||
|
if (! $orientationHeight) {
|
||||||
|
expect(in_array($number, ['600', '840', '1200', '1600'], true))->toBeTrue("{$query} is not at an M3 breakpoint");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -329,31 +358,77 @@ it('declares M3\'s spacing scale as the reference gives it', function () {
|
|||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The layout components' stylesheets: layout.css and everything it imports.
|
* The layout components' stylesheets: all.css's own `./layout/*.css` imports, each with
|
||||||
|
* everything it in turn imports (which reaches into `components/`, for the layout files that draw
|
||||||
|
* with a component, e.g. pane.css's app bar and button).
|
||||||
*
|
*
|
||||||
* @return list<string>
|
* @return list<string>
|
||||||
*/
|
*/
|
||||||
function layoutStylesheets(): array
|
function layoutStylesheets(): array
|
||||||
{
|
{
|
||||||
return stylesheetTree(stylesheetPath('layout.css'));
|
$files = [];
|
||||||
|
|
||||||
|
foreach (stylesheetImports(stylesheetPath('all.css')) as $import) {
|
||||||
|
if (! str_starts_with($import, './layout/')) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (stylesheetTree(stylesheetPath($import)) as $file) {
|
||||||
|
if (! in_array($file, $files, true)) {
|
||||||
|
$files[] = $file;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $files;
|
||||||
}
|
}
|
||||||
|
|
||||||
it('imports a stylesheet for every layout component from layout.css, each beside its view', function () {
|
it('imports every stylesheet under components/ and layout/ directly, each exactly once', function () {
|
||||||
$imports = stylesheetImports(stylesheetPath('layout.css'));
|
$imports = stylesheetImports(stylesheetPath('all.css'));
|
||||||
|
|
||||||
|
expect(count($imports))->toBe(count(array_unique($imports)), 'all.css imports something more than once');
|
||||||
|
|
||||||
|
$expected = collect(File::files(stylesheetPath('components')))
|
||||||
|
->map(fn (SplFileInfo $file): string => "./components/{$file->getFilename()}")
|
||||||
|
->merge(collect(File::files(stylesheetPath('layout')))->map(fn (SplFileInfo $file): string => "./layout/{$file->getFilename()}"))
|
||||||
|
->sort()
|
||||||
|
->values()
|
||||||
|
->all();
|
||||||
|
|
||||||
|
// Every file under both directories is imported directly — a shared one (navigation-item.css,
|
||||||
|
// selection.css, menu.css included) as much as a component's own — and nothing stale remains.
|
||||||
|
$actual = collect($imports)
|
||||||
|
->filter(fn (string $import): bool => str_starts_with($import, './components/') || str_starts_with($import, './layout/'))
|
||||||
|
->sort()
|
||||||
|
->values()
|
||||||
|
->all();
|
||||||
|
|
||||||
|
expect($actual)->toBe($expected);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('imports a stylesheet for every layout component from all.css, each beside its view', function () {
|
||||||
|
$imports = array_values(array_filter(
|
||||||
|
stylesheetImports(stylesheetPath('all.css')),
|
||||||
|
fn (string $import): bool => str_starts_with($import, './layout/'),
|
||||||
|
));
|
||||||
$reached = array_map(stylesheetName(...), layoutStylesheets());
|
$reached = array_map(stylesheetName(...), layoutStylesheets());
|
||||||
|
|
||||||
$files = collect(File::files(stylesheetPath('layout')))->map(fn (SplFileInfo $file): string => $file->getBasename('.css'));
|
$files = collect(File::files(stylesheetPath('layout')))->map(fn (SplFileInfo $file): string => $file->getBasename('.css'));
|
||||||
|
|
||||||
// Two files are shared by the components rather than being one: the spacing props and the
|
// Two files are shared by the components rather than being one: the spacing props and the
|
||||||
// visibility props.
|
// visibility props. Both are still imported directly by all.css, like every other layout file
|
||||||
|
// — only the "has its own component" half of the check below skips them.
|
||||||
$components = $files->diff(['spacing', 'visibility'])->values();
|
$components = $files->diff(['spacing', 'visibility'])->values();
|
||||||
|
|
||||||
expect($components)->not->toBeEmpty()
|
expect($components)->not->toBeEmpty()
|
||||||
->and($files->map(fn (string $name): string => "layout/{$name}.css")->diff($reached)->values()->all())->toBe([]);
|
->and($files->map(fn (string $name): string => "layout/{$name}.css")->diff($reached)->values()->all())->toBe([]);
|
||||||
|
|
||||||
|
foreach ($files as $name) {
|
||||||
|
expect(in_array("./layout/{$name}.css", $imports, true))->toBeTrue("layout/{$name}.css is not imported directly by all.css");
|
||||||
|
}
|
||||||
|
|
||||||
foreach ($components as $name) {
|
foreach ($components as $name) {
|
||||||
expect(File::exists(__DIR__."/../../resources/views/components/{$name}.blade.php"))->toBeTrue("layout/{$name}.css has no component")
|
expect(File::exists(__DIR__."/../../resources/views/components/{$name}.blade.php"))->toBeTrue("layout/{$name}.css has no component");
|
||||||
->and($imports)->toContain("./layout/{$name}.css");
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -389,28 +464,10 @@ it('imports, from every layout stylesheet, the stylesheet of each component its
|
|||||||
expect($checked)->toBeGreaterThan(0);
|
expect($checked)->toBeGreaterThan(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('opens every layout stylesheet with a header, the layer statement and plain imports', function () {
|
// Every layout stylesheet's header, layer statement and plain-imports shape, and its freedom from
|
||||||
foreach (layoutStylesheets() as $file) {
|
// Tailwind and from a media query off an M3 breakpoint, are covered by the two all.css-wide tests
|
||||||
$name = stylesheetName($file);
|
// above (allStylesheets() reaches every layout file too) — a dedicated layout-only version of
|
||||||
$items = stylesheetItems(File::get($file));
|
// either would only repeat that same check on a subset already checked.
|
||||||
|
|
||||||
expect(File::get($file))->toStartWith('/*', "{$name} has no header comment")
|
|
||||||
->and($items[0]['statement'] ?? null)->toBe(MATERIAL_LAYER_STATEMENT, "{$name} does not open with the layer statement");
|
|
||||||
|
|
||||||
$blocks = false;
|
|
||||||
|
|
||||||
foreach (array_slice($items, 1) as $item) {
|
|
||||||
if (isset($item['prelude'])) {
|
|
||||||
$blocks = true;
|
|
||||||
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
expect($blocks)->toBeFalse("{$name}: `{$item['statement']}` comes after a block")
|
|
||||||
->and($item['statement'])->toMatch('/^@import ([\'"])\.{1,2}\/[\w.\/-]+\.css\1;$/', "{$name}: `{$item['statement']}` is not a plain import");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
it('keeps every layout rule in material.layout, and the visibility props in material.visibility', function () {
|
it('keeps every layout rule in material.layout, and the visibility props in material.visibility', function () {
|
||||||
// layoutStylesheets() follows every @import, so it now reaches component stylesheets a layout
|
// layoutStylesheets() follows every @import, so it now reaches component stylesheets a layout
|
||||||
@@ -442,36 +499,6 @@ it('keeps every layout rule in material.layout, and the visibility props in mate
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
it('writes no Tailwind in the layout stylesheets, and media queries only at M3\'s breakpoints, in px', function () {
|
|
||||||
$queries = 0;
|
|
||||||
|
|
||||||
// Component stylesheets reached through a layout file (see the test above) are not layout
|
|
||||||
// stylesheets themselves, and keep their own group's rules (a hover query included).
|
|
||||||
foreach (layoutStylesheets() as $file) {
|
|
||||||
$name = stylesheetName($file);
|
|
||||||
|
|
||||||
if (! str_starts_with($name, 'layout/')) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
$css = stylesheetWithoutComments(File::get($file));
|
|
||||||
|
|
||||||
expect($css)
|
|
||||||
->not->toMatch('/@(?:tailwind|theme|utility|variant|custom-variant|apply|source|config|plugin|reference)\b/', $name)
|
|
||||||
->not->toMatch('/--(?:theme|spacing|alpha)\(|\btheme\(|[\'"]tailwindcss[\'"]/', $name);
|
|
||||||
|
|
||||||
preg_match_all('/@media\b([^{]*)\{/', $css, $matches);
|
|
||||||
|
|
||||||
foreach ($matches[1] as $query) {
|
|
||||||
$queries++;
|
|
||||||
|
|
||||||
expect(trim($query))->toMatch('/^\(width (?:<|>=) (?:600|840|1200|1600)px\)$/', "{$name}: `@media {$query}` is not an M3 breakpoint in px");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
expect($queries)->toBeGreaterThan(0);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('gives every spacing token a gap and a padding, and the layout components read only those', function () {
|
it('gives every spacing token a gap and a padding, and the layout components read only those', function () {
|
||||||
$tokens = array_map(
|
$tokens = array_map(
|
||||||
fn (string $property): string => substr($property, strlen('--md-sys-measurement-')),
|
fn (string $property): string => substr($property, strlen('--md-sys-measurement-')),
|
||||||
@@ -610,13 +637,16 @@ it('declares the material layers in the Workbench between Tailwind\'s preflight
|
|||||||
// otherwise beat a rewritten component's padding, and below the utilities.
|
// otherwise beat a rewritten component's padding, and below the utilities.
|
||||||
expect(trim($app))->toStartWith('@layer theme, base, material, components, utilities;');
|
expect(trim($app))->toStartWith('@layer theme, base, material, components, utilities;');
|
||||||
|
|
||||||
$foundation = strpos($app, "@import '../../../resources/css/foundation.css';");
|
$all = strpos($app, "@import '../../../resources/css/all.css';");
|
||||||
$tailwind = strpos($app, "@import 'tailwindcss'");
|
$tailwind = strpos($app, "@import 'tailwindcss'");
|
||||||
$components = strpos($app, "@import '../../../resources/css/tailwind.css';");
|
$components = strpos($app, "@import '../../../resources/css/tailwind.css';");
|
||||||
|
|
||||||
expect($foundation)->toBeInt()
|
expect($all)->toBeInt()
|
||||||
->and($tailwind)->toBeGreaterThan($foundation)
|
->and($tailwind)->toBeGreaterThan($all)
|
||||||
->and($components)->toBeGreaterThan($tailwind)
|
->and($components)->toBeGreaterThan($tailwind)
|
||||||
// material.css would declare the tokens a second time.
|
// material.css declared the tokens a second time; components.css and layout.css are the
|
||||||
->and($app)->not->toContain('material.css');
|
// interim lists all.css replaced.
|
||||||
|
->and($app)->not->toContain('material.css')
|
||||||
|
->not->toContain("resources/css/components.css'")
|
||||||
|
->not->toContain("resources/css/layout.css'");
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Support\Facades\File;
|
||||||
use NoNameWeb\LivewireMaterial\Tests\TestCase;
|
use NoNameWeb\LivewireMaterial\Tests\TestCase;
|
||||||
|
|
||||||
pest()->extend(TestCase::class)->in('Feature', 'Browser');
|
pest()->extend(TestCase::class)->in('Feature', 'Browser');
|
||||||
@@ -24,3 +25,27 @@ function layoutRoot(string $html): array
|
|||||||
if (($timeout = (int) getenv('BROWSER_TIMEOUT')) > 0) {
|
if (($timeout = (int) getenv('BROWSER_TIMEOUT')) > 0) {
|
||||||
pest()->browser()->timeout($timeout);
|
pest()->browser()->timeout($timeout);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The named block of resources/css/all.css — the content between its `/* Heading *\/` comment and
|
||||||
|
* the next one, or the end of the file for the last block ("Navigation"). components.css and
|
||||||
|
* layout.css grouped their imports the same way before plan step 37 folded both into all.css; this
|
||||||
|
* is the one place that block lookup lives now, so a stylesheet test asks "is this imported from
|
||||||
|
* the Containment block" without repeating the search in every file that needs it. It is here,
|
||||||
|
* rather than in tests/Feature/StylesheetsTest.php, because Pest.php is always loaded, whichever
|
||||||
|
* test file or path is run.
|
||||||
|
*/
|
||||||
|
function allCssBlock(string $heading): string
|
||||||
|
{
|
||||||
|
$all = File::get(__DIR__.'/../resources/css/all.css');
|
||||||
|
$marker = "/* {$heading} */";
|
||||||
|
$start = strpos($all, $marker);
|
||||||
|
|
||||||
|
if ($start === false) {
|
||||||
|
throw new RuntimeException("all.css has no `{$marker}` block.");
|
||||||
|
}
|
||||||
|
|
||||||
|
$next = strpos($all, '/*', $start + strlen($marker));
|
||||||
|
|
||||||
|
return $next === false ? substr($all, $start) : substr($all, $start, $next - $start);
|
||||||
|
}
|
||||||
|
|||||||
@@ -2,12 +2,12 @@
|
|||||||
in docs/ are not scanned for class names. The layer statement puts the `material` layers above
|
in docs/ are not scanned for class names. The layer statement puts the `material` layers above
|
||||||
Tailwind's preflight (`base`) and below its `components` and `utilities`: a rewritten component's
|
Tailwind's preflight (`base`) and below its `components` and `utilities`: a rewritten component's
|
||||||
padding, margin and border would otherwise lose to preflight's `* { padding: 0 }`, which the
|
padding, margin and border would otherwise lose to preflight's `* { padding: 0 }`, which the
|
||||||
material reset already carries, while every utility still outranks the package. tailwind.css
|
material reset already carries, while every utility still outranks the package. all.css brings
|
||||||
brings the components still written for Tailwind, without declaring the tokens a second time. */
|
the foundation, every layout stylesheet and every component stylesheet in one import; tailwind.css
|
||||||
|
brings the theme and token utilities the showcase still writes as classes, without declaring the
|
||||||
|
tokens a second time. */
|
||||||
@layer theme, base, material, components, utilities;
|
@layer theme, base, material, components, utilities;
|
||||||
@import '../../../resources/css/foundation.css';
|
@import '../../../resources/css/all.css';
|
||||||
@import '../../../resources/css/layout.css';
|
|
||||||
@import '../../../resources/css/components.css';
|
|
||||||
@import 'tailwindcss' source(none);
|
@import 'tailwindcss' source(none);
|
||||||
@import '../../../resources/css/tailwind.css';
|
@import '../../../resources/css/tailwind.css';
|
||||||
@import './material-scheme.css';
|
@import './material-scheme.css';
|
||||||
|
|||||||
Reference in New Issue
Block a user