Collapse the button-variant fork and the canvas's duplicate work

LoadingButton kept its own copy of buttonVariants, which had drifted past
`brand` and the icon sizes; it imports Button's now, so the Connect /
Re-pair button can show the loader instead of swapping its own label.

DashboardView mounted a second LookProvider inside the one CanvasSurface
already gives it and dropped `touch` on the way, so every widget on a
touch panel read `useLook().touch` as false. The provider is now mounted
once, by whatever owns the canvas root.

useCanvasRoot resolved the palette twice per render — once for the theme
class, once for the tokens — so useDashboardTheme takes the roles the
caller already has.

The bar assertions in widgets.spec.ts are scoped to the Load tile rather
than the page, which held only because the unbound spare bar draws no
rows.
This commit is contained in:
2026-09-14 15:29:40 +02:00
parent 4faf5312ff
commit 57fd3d1b2c
7 changed files with 50 additions and 60 deletions
+16 -6
View File
@@ -302,16 +302,20 @@ async function openPanel(page: Page, name = dashboardName) {
)
}
/** The Load tile, scoped: the panel carries a second bar (the unbound one). */
const loadTile = (page: Page) =>
page.getByTestId("widget-frame").filter({ hasText: "Load" })
test("a bar draws a row per reading", async ({ page }) => {
await openPanel(page)
// The panel's bar is stored the way a bar was written before it had rows —
// one reading with a second nested in it — so drawing two rows is also what
// proves that shape is still read.
const rows = page.getByTestId("bar-row")
const rows = loadTile(page).getByTestId("bar-row")
await expect(rows).toHaveCount(2)
const fills = page.getByTestId("bar-fill")
const fills = loadTile(page).getByTestId("bar-fill")
const width = (index: number) =>
fills
.nth(index)
@@ -355,11 +359,17 @@ test("a reading over its scale fills the track and no more", async ({
// bounding box is not clipped by an ancestor's overflow. Where it settles is
// what "fills the track and no more" is about, so that is what is measured:
// flush with the track, neither short of it nor over.
const track = page.getByTestId("bar-row").nth(1).locator(".dui-bar-track")
const track = loadTile(page)
.getByTestId("bar-row")
.nth(1)
.locator(".dui-bar-track")
await expect
.poll(
async () => {
const fill = await page.getByTestId("bar-fill").nth(1).boundingBox()
const fill = await loadTile(page)
.getByTestId("bar-fill")
.nth(1)
.boundingBox()
const rail = await track.boundingBox()
return Math.round(fill!.width - rail!.width)
},
@@ -705,7 +715,7 @@ for (const scheme of ["light", "dark"] as const) {
)
// Every widget still draws, whichever set is asked to draw it.
await expect(page.getByTestId("widget-frame")).toHaveCount(TILES)
await expect(page.getByTestId("bar-row")).toHaveCount(2)
await expect(loadTile(page).getByTestId("bar-row")).toHaveCount(2)
await page.screenshot({
path: `screenshots/widgets/${scheme}-${look}.png`,
@@ -738,7 +748,7 @@ for (const scheme of ["light", "dark"] as const) {
expect(Math.round(ground.width)).toBe(Math.round(canvas.width))
expect(Math.round(ground.height)).toBe(Math.round(canvas.height))
const primary = await page
const primary = await loadTile(page)
.getByTestId("bar-fill")
.first()
.evaluate((el) => getComputedStyle(el).backgroundColor)