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:
+2
-7
@@ -100,13 +100,8 @@ Deferring because out of scope is fine, but don't mention deferring than.
|
|||||||
across all 16 columns. Another tile needs a rearrangement first — worth doing together
|
across all 16 columns. Another tile needs a rearrangement first — worth doing together
|
||||||
with re-authoring `PAGES` as a single section.
|
with re-authoring `PAGES` as a single section.
|
||||||
- CHORE/TEST: no Playwright coverage for the colour tile — `tests/widgets.spec.ts` covers the
|
- CHORE/TEST: no Playwright coverage for the colour tile — `tests/widgets.spec.ts` covers the
|
||||||
other controls. Wants a spec that drags the hue ring and asserts one publish on release.
|
other controls. Wants a spec that drags the disc and asserts one publish on release. The
|
||||||
- CHORE/UI: the colour wheel's arrow keys move a fixed 5°, with no PageUp/PageDown or
|
maths is checked (`color.check.ts`); what is not is the gesture.
|
||||||
Home/End. Fine for picking by eye; a panel that wants an exact hue has no coarse/fine step.
|
|
||||||
- CHORE/UI: the colour wheel reads the pointer's angle only, never its distance from the
|
|
||||||
centre (marked `ponytail:` in `ColorWidget.tsx`), so a colour takes two gestures — the ring,
|
|
||||||
then saturation. A saturation-by-radius disc would need a second axis on a control that can
|
|
||||||
announce one value.
|
|
||||||
- CHORE/DEMO: `seed_demo.py` still writes the Home dashboard as three titled sections
|
- CHORE/DEMO: `seed_demo.py` still writes the Home dashboard as three titled sections
|
||||||
("Right now", "Energy and comfort", "Yield model"). The editor and the panel now read a
|
("Right now", "Energy and comfort", "Yield model"). The editor and the panel now read a
|
||||||
page's sections as one grid, so those headings are no longer drawn and the first editor
|
page's sections as one grid, so those headings are no longer drawn and the first editor
|
||||||
|
|||||||
@@ -55,7 +55,6 @@ export function ColorWidget({ widget, dashboard }: WidgetProps) {
|
|||||||
// The pulse hangs off the frame, so it stays outside every box below.
|
// The pulse hangs off the frame, so it stays outside every box below.
|
||||||
<>
|
<>
|
||||||
{pulse}
|
{pulse}
|
||||||
<div className="min-h-0 flex-1">
|
|
||||||
<ColorDisk
|
<ColorDisk
|
||||||
name={widget.title || target}
|
name={widget.title || target}
|
||||||
hsv={current}
|
hsv={current}
|
||||||
@@ -67,7 +66,6 @@ export function ColorWidget({ widget, dashboard }: WidgetProps) {
|
|||||||
setDraft(null)
|
setDraft(null)
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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.
|
* The colour disc.
|
||||||
*
|
*
|
||||||
@@ -132,6 +155,7 @@
|
|||||||
position: relative;
|
position: relative;
|
||||||
width: min(100cqh, 100cqw - 3.5rem);
|
width: min(100cqh, 100cqw - 3.5rem);
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
|
max-height: 100cqh;
|
||||||
aspect-ratio: 1;
|
aspect-ratio: 1;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
touch-action: none;
|
touch-action: none;
|
||||||
|
|||||||
@@ -177,7 +177,7 @@ export function ColorDisk({
|
|||||||
})
|
})
|
||||||
const [hue, saturation, brightness] = hsv
|
const [hue, saturation, brightness] = hsv
|
||||||
return (
|
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
|
<div
|
||||||
{...discProps}
|
{...discProps}
|
||||||
data-testid={TESTID.disc}
|
data-testid={TESTID.disc}
|
||||||
|
|||||||
@@ -171,7 +171,7 @@ export function ColorDisk({
|
|||||||
})
|
})
|
||||||
const [hue, saturation, brightness] = hsv
|
const [hue, saturation, brightness] = hsv
|
||||||
return (
|
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
|
<div
|
||||||
{...discProps}
|
{...discProps}
|
||||||
data-testid={TESTID.disc}
|
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)
|
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 }) => {
|
test("the icon follows what the message says", async ({ page }) => {
|
||||||
await openPanel(page)
|
await openPanel(page)
|
||||||
const glyph = page.getByTestId("icon-glyph")
|
const glyph = page.getByTestId("icon-glyph")
|
||||||
@@ -461,6 +477,37 @@ test("a chart's cursor follows the pointer", async ({ page }) => {
|
|||||||
).toBeLessThan(3)
|
).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.
|
* What a dashboard was told to wear, as a wall panel would be told.
|
||||||
*
|
*
|
||||||
|
|||||||
Reference in New Issue
Block a user