ui: test the last four components, and put the last four in the gallery

Closes the tail of the library: `Loader`, `Loading`, `Placeholder` and
`Checkbox` had no test of their own, and `IconStack`, `MediaFilterToggle`,
`MultiSelect` and `NumberInput` were not in the gallery, which the project
rule says every component must be.

Putting them in the gallery is what found the rest of this.

An IconStack's corner glyph paints a disc of the surface behind itself so
the two sets of strokes do not cross, and that surface was hardcoded to the
page background.  It is right in exactly one of the three places a stack
appears.  The nav bar showed a white disc on the red bar, and the file
browser's "New Folder" button showed one punched through the blue fill.  It
is now `--icon-stack-bg`, defaulting to the page: the file browser passes
the button's fill, and the nav bar sets it on the <nav> beside the colour it
already writes there, so it inherits -- the bar's colour is a user setting
and cannot be named by a token.

A MultiSelect's chosen options are Mantine Pills, and Pill is the one
component that ignores the radius theme: it writes `--pill-radius: rem(1000)`
onto the element, which zeroing `--mantine-radius-xl` cannot reach.  They
were lozenges on a page of square boxes.

Twelve jest tests and sixteen browser tests.  The browser half covers what
jsdom cannot judge: that a Skeleton is a different colour from the panel it
covers in all four themes (its greys are remapped to our tokens, and neither
`--border` nor `--bg` was picked with a panel behind it), that the loader
colour resolves through the token, and that the corner disc matches its
surface.  Every one was checked red against a planted regression first.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Roland 2026-08-01 15:48:10 -06:00 • committed by lrnselfreliance
parent 3315510708
commit 2c812b5410
7 changed files with 392 additions and 13 deletions

View file

@ -35,13 +35,18 @@
/*
* The corner glyph sits on the main one, so it carries the surface color behind
* it — without that the two sets of strokes cross and neither reads.
*
* Which surface that is depends on where the stack was placed, and only the call
* site knows: the file browser puts one inside a filled blue button, where a disc
* of the page background is a hole punched in the button. `--icon-stack-bg`
* defaults to the page, and a call site anywhere else sets it.
*/
.wrolpi-icon-stack-corner {
position: absolute;
right: -3px;
bottom: -3px;
display: flex;
background: var(--bg);
background: var(--icon-stack-bg, var(--bg));
border-radius: 50%;
padding: 1px;
}
@ -357,6 +362,17 @@ html[data-theme="amber"] .wrolpi-searchbox-control:focus-within {
}
/* ------------------------------------------------------------- Form fields */
/*
* A Pill is what MultiSelect renders for each chosen option, and it is the one place
* Mantine does not honour the radius theme: `Pill` writes `--pill-radius: rem(1000)`
* on the element itself, which no amount of zeroing `--mantine-radius-xl` can reach.
* Everything else in the library has hard corners, so these do too.
*/
.mantine-Pill-root {
--pill-radius: 0;
border-radius: 0;
}
/*
* `hooks/useForm.js` renders its own <label> and native <input> — it always did, with
* Semantic's Form.Input only wrapping them for field styling. These rules supply that