feat(frontend): serialize/read helpers for plot config

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Experiments DB Dev
2026-06-01 10:16:16 -04:00
parent e9511bdac3
commit 4b82df514f
2 changed files with 127 additions and 0 deletions
+59
View File
@@ -50,3 +50,62 @@ export function groupLines(lines) {
}
return order.map((group) => ({ group, subjects: byGroup.get(group) }));
}
// ── Plot config (de)serialization ───────────────────────────────────────────────
// Translate between ExperimentAnalysisCharts React state and the stored JSONB blob.
// readPlotConfig is defensive: it never throws and fills safe defaults for missing
// or malformed input (unknown keys are ignored).
// Symmetric with readChart so a serialize→read round-trip is lossless.
function snapshotChart(s = {}) {
const str = (v) => (typeof v === 'string' ? v : '');
return {
height: Number.isFinite(s.height) ? s.height : undefined,
yMin: str(s.yMin),
yMax: str(s.yMax),
xZoomMin: str(s.xZoomMin),
xZoomMax: str(s.xZoomMax),
xFieldOverride: typeof s.xFieldOverride === 'string' ? s.xFieldOverride : null,
};
}
export function serializePlotConfig(state = {}) {
const { xField, groupBy, tickStep, metric, hiddenSubjects, sidebarOpen, counts, rate } = state;
return {
xField,
groupBy,
tickStep,
metric,
hiddenSubjects: [...(hiddenSubjects ?? [])].sort(),
sidebarOpen: !!sidebarOpen,
counts: snapshotChart(counts),
rate: snapshotChart(rate),
};
}
function readChart(c) {
const o = c && typeof c === 'object' && !Array.isArray(c) ? c : {};
const str = (v) => (typeof v === 'string' ? v : '');
return {
height: Number.isFinite(o.height) ? o.height : undefined,
yMin: str(o.yMin),
yMax: str(o.yMax),
xZoomMin: str(o.xZoomMin),
xZoomMax: str(o.xZoomMax),
xFieldOverride: typeof o.xFieldOverride === 'string' ? o.xFieldOverride : null,
};
}
export function readPlotConfig(raw) {
const c = raw && typeof raw === 'object' && !Array.isArray(raw) ? raw : {};
return {
xField: typeof c.xField === 'string' ? c.xField : undefined,
groupBy: typeof c.groupBy === 'string' ? c.groupBy : undefined,
tickStep: Number.isFinite(c.tickStep) ? c.tickStep : undefined,
metric: typeof c.metric === 'string' ? c.metric : undefined,
hiddenSubjects: Array.isArray(c.hiddenSubjects) ? c.hiddenSubjects.filter((x) => typeof x === 'string') : [],
sidebarOpen: c.sidebarOpen === true,
counts: readChart(c.counts),
rate: readChart(c.rate),
};
}