A dashboard names the data colours its charts draw with: an ordered, distinct
subset of the `--chart-1…5` ramp, stored on the settings channel that already
carries `theme` and `locked`. No backend or client change — `SettingDef.value`
is free-form and a name this build does not wire up is left alone rather than
refused.
Naming none is the whole ramp, which resolves to the identical token per
series as the `--chart-${(index % 5) + 1}` charts drew with before, so every
existing dashboard is unaffected. `palette.check.ts` asserts that equivalence
rather than trusting it.
Distinct slots, not free assignment with repeats: only slot 1 against slot 5
clears 3:1 within the ramp, so two traces on one slot could not be told apart.
Status colour is deliberately outside the palette — a fault is `--destructive`
because of what it means, not because of where it sits.
The provider is mounted by the editor as well as the view, so picking a
palette repaints the charts beside the panel instead of describing them.
82 lines
2.5 KiB
TypeScript
82 lines
2.5 KiB
TypeScript
/**
|
|
* The dashboard palette, checked.
|
|
*
|
|
* ponytail: a script rather than a suite, like `color.check.ts` beside it —
|
|
* the frontend's only runner is Playwright and picking a ramp slot does not
|
|
* need a browser:
|
|
*
|
|
* cd frontend && bun run src/components/Dashboard/palette.check.ts
|
|
*
|
|
* The first block is the one that matters. A palette changes how *every*
|
|
* existing dashboard is drawn, and the whole safety argument is that a
|
|
* dashboard which names none is drawn exactly as it was before palettes
|
|
* existed — `--chart-${(index % 5) + 1}`, the literal expression `seriesColor`
|
|
* used. That equivalence is asserted here rather than trusted.
|
|
*/
|
|
|
|
import assert from "node:assert/strict"
|
|
|
|
import {
|
|
CHART_SLOTS,
|
|
DEFAULT_PALETTE,
|
|
paletteOf,
|
|
slotFor,
|
|
} from "@/components/Common/UplotChart"
|
|
|
|
/** What a chart drew before a dashboard could name a palette. */
|
|
const before = (index: number) => String((index % 5) + 1)
|
|
|
|
// A document with no palette, and every shape a broken one can arrive in:
|
|
// all of them fall back to the ramp, and the ramp is the old expression.
|
|
for (const stored of [undefined, null, "", [], ["9", "nonsense"], { 0: "1" }]) {
|
|
const palette = paletteOf(stored)
|
|
assert.deepEqual(
|
|
palette,
|
|
DEFAULT_PALETTE,
|
|
`${JSON.stringify(stored)} is no palette`,
|
|
)
|
|
for (let index = 0; index < 23; index++) {
|
|
assert.equal(
|
|
slotFor(index, palette),
|
|
before(index),
|
|
`series ${index} of an unset palette draws as it always did`,
|
|
)
|
|
}
|
|
}
|
|
|
|
// The same, for a chart that was handed nothing at all — Health and Home draw
|
|
// through the very same component.
|
|
for (let index = 0; index < 23; index++) {
|
|
assert.equal(
|
|
slotFor(index),
|
|
before(index),
|
|
`series ${index} outside a dashboard`,
|
|
)
|
|
}
|
|
|
|
// A distinct subset in draw order: kept as picked, and cycled through.
|
|
assert.deepEqual(
|
|
paletteOf(["3", "1"]),
|
|
["3", "1"],
|
|
"the order picked is the order kept",
|
|
)
|
|
assert.deepEqual(
|
|
[0, 1, 2, 3, 4].map((index) => slotFor(index, ["3", "1"])),
|
|
["3", "1", "3", "1", "3"],
|
|
"a chart with more lines than colours starts over",
|
|
)
|
|
|
|
// Repeats are dropped rather than drawn: two lines on one slot could not be
|
|
// told apart, which is the whole reason the picker offers each slot once.
|
|
assert.deepEqual(paletteOf(["2", "2", "4"]), ["2", "4"], "a repeat is dropped")
|
|
assert.deepEqual(paletteOf([1, 2]), ["1", "2"], "numbers name slots too")
|
|
assert.deepEqual(paletteOf([...CHART_SLOTS].reverse()), [
|
|
"5",
|
|
"4",
|
|
"3",
|
|
"2",
|
|
"1",
|
|
])
|
|
|
|
console.log("palette: ok")
|