Draw the colour disc inside its tile rather than past it
Docs / docs (push) Successful in 42s
Playwright Tests / test-playwright (1, 2) (push) Failing after 1m10s
Playwright Tests / test-playwright (2, 2) (push) Failing after 22s
pre-commit / pre-commit (push) Failing after 2m7s
Test Backend / test-backend (push) Failing after 48s
Compose Smoke Test / test-compose (push) Failing after 22s
Playwright Tests / merge-reports (push) Failing after 1m6s
Docs / docs (push) Successful in 42s
Playwright Tests / test-playwright (1, 2) (push) Failing after 1m10s
Playwright Tests / test-playwright (2, 2) (push) Failing after 22s
pre-commit / pre-commit (push) Failing after 2m7s
Test Backend / test-backend (push) Failing after 48s
Compose Smoke Test / test-compose (push) Failing after 22s
Playwright Tests / merge-reports (push) Failing after 1m6s
The disc was sized against its parents in percentages, and every box between it and the tile is sized by what is in it — so the chain never resolved, the brightness slider grew, and the tile scrolled with half a disc showing. Container units are the tile's own height whatever sits in between, so the widget now fits exactly. Two guards behind it, since both are things a picture can fail silently: nothing on a panel of pictures may overflow its tile, and Touch has to actually make a control bigger while leaving what it publishes alone.
This commit is contained in:
@@ -55,19 +55,17 @@ export function ColorWidget({ widget, dashboard }: WidgetProps) {
|
||||
// The pulse hangs off the frame, so it stays outside every box below.
|
||||
<>
|
||||
{pulse}
|
||||
<div className="min-h-0 flex-1">
|
||||
<ColorDisk
|
||||
name={widget.title || target}
|
||||
hsv={current}
|
||||
disabled={locked}
|
||||
onChange={setDraft}
|
||||
onCommit={() => {
|
||||
if (draft === null) return
|
||||
send(encodeColor(draft, format))
|
||||
setDraft(null)
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<ColorDisk
|
||||
name={widget.title || target}
|
||||
hsv={current}
|
||||
disabled={locked}
|
||||
onChange={setDraft}
|
||||
onCommit={() => {
|
||||
if (draft === null) return
|
||||
send(encodeColor(draft, format))
|
||||
setDraft(null)
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -118,6 +118,29 @@
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* The colour widget: a disc, and brightness beside it.
|
||||
*
|
||||
* Sized in container units rather than percentages. A percentage height only
|
||||
* resolves against a parent whose own height is already known, and every box
|
||||
* between here and the tile is sized by its contents — so the chain never
|
||||
* resolved and the slider grew until the tile scrolled. `cqh` is the tile's
|
||||
* height whatever is in between.
|
||||
*/
|
||||
.dui-color {
|
||||
display: grid;
|
||||
height: 100cqh;
|
||||
min-height: 0;
|
||||
grid-template-columns: minmax(0, 1fr) auto;
|
||||
align-items: center;
|
||||
justify-items: center;
|
||||
gap: 0.75rem;
|
||||
}
|
||||
|
||||
.dui-color > .dui-slider {
|
||||
height: 100cqh;
|
||||
}
|
||||
|
||||
/*
|
||||
* The colour disc.
|
||||
*
|
||||
@@ -132,6 +155,7 @@
|
||||
position: relative;
|
||||
width: min(100cqh, 100cqw - 3.5rem);
|
||||
max-width: 100%;
|
||||
max-height: 100cqh;
|
||||
aspect-ratio: 1;
|
||||
border-radius: 50%;
|
||||
touch-action: none;
|
||||
|
||||
@@ -177,7 +177,7 @@ export function ColorDisk({
|
||||
})
|
||||
const [hue, saturation, brightness] = hsv
|
||||
return (
|
||||
<div className="grid h-full min-h-0 grid-cols-[minmax(0,1fr)_auto] items-center justify-items-center gap-3">
|
||||
<div className="dui-color">
|
||||
<div
|
||||
{...discProps}
|
||||
data-testid={TESTID.disc}
|
||||
|
||||
@@ -171,7 +171,7 @@ export function ColorDisk({
|
||||
})
|
||||
const [hue, saturation, brightness] = hsv
|
||||
return (
|
||||
<div className="grid h-full min-h-0 grid-cols-[minmax(0,1fr)_auto] items-center justify-items-center gap-3">
|
||||
<div className="dui-color">
|
||||
<div
|
||||
{...discProps}
|
||||
data-testid={TESTID.disc}
|
||||
|
||||
@@ -306,6 +306,22 @@ test("rows are drawn in the order they were configured", async ({ page }) => {
|
||||
await expect(rows.nth(2)).toContainText(/grid/i)
|
||||
})
|
||||
|
||||
test("a widget is drawn inside its tile rather than scrolled", async ({
|
||||
page,
|
||||
}) => {
|
||||
await openPanel(page)
|
||||
|
||||
// A picture that overflows its tile is a picture nobody can see the rest of.
|
||||
// Only the widgets that are text or a list may scroll, and this panel holds
|
||||
// none of them — so nothing here should.
|
||||
const overflowing = await page.evaluate(() =>
|
||||
[...document.querySelectorAll(".dui-frame-body")]
|
||||
.filter((body) => body.scrollHeight > body.clientHeight + 1)
|
||||
.map((body) => body.closest("[data-testid=widget-frame]")?.textContent),
|
||||
)
|
||||
expect(overflowing, "these widgets overflow their tile").toEqual([])
|
||||
})
|
||||
|
||||
test("the icon follows what the message says", async ({ page }) => {
|
||||
await openPanel(page)
|
||||
const glyph = page.getByTestId("icon-glyph")
|
||||
@@ -461,6 +477,37 @@ test("a chart's cursor follows the pointer", async ({ page }) => {
|
||||
).toBeLessThan(3)
|
||||
})
|
||||
|
||||
test("touch makes the controls bigger without changing what they do", async ({
|
||||
page,
|
||||
}) => {
|
||||
await openPanel(page)
|
||||
const control = page.getByTestId("widget-frame").filter({ hasText: "Mode" })
|
||||
const pointer = (await control.getByRole("button").first().boundingBox())!
|
||||
.height
|
||||
|
||||
await setLook(page, dashboardName, { touch: { value: true } })
|
||||
await openPanel(page)
|
||||
await expect(page.getByTestId("canvas-surface")).toHaveAttribute(
|
||||
"data-touch",
|
||||
"",
|
||||
)
|
||||
const touched = (await control.getByRole("button").first().boundingBox())!
|
||||
.height
|
||||
expect(
|
||||
touched,
|
||||
`a segment is ${touched.toFixed(1)} touched and ${pointer.toFixed(1)} pointed at`,
|
||||
).toBeGreaterThan(pointer)
|
||||
|
||||
// The control is the same control: it still publishes what it always did.
|
||||
await control.getByRole("button", { name: "Boost" }).click()
|
||||
await expect(control.getByRole("button", { name: "Boost" })).toHaveAttribute(
|
||||
"aria-pressed",
|
||||
"true",
|
||||
)
|
||||
|
||||
await setLook(page, dashboardName, { touch: { value: false } })
|
||||
})
|
||||
|
||||
/**
|
||||
* What a dashboard was told to wear, as a wall panel would be told.
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user