Skip to content
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 40 additions & 3 deletions app/src/lib/__tests__/chart-registry.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1491,16 +1491,53 @@ describe("radar transform", () => {
expect(result.indicators[0].name).toBe("X");
});

it("auto-scales max from data when max column is missing", () => {
it("auto-scales max from data when max column is missing (single indicator)", () => {
const data = [{ indicator: "Speed", value: 80 }];
const result = transform(data) as { indicators: Array<{ name: string; max: number }>; series: unknown[] };
// 80 * 1.1 = 88, ceil → 88
expect(result.indicators[0].max).toBe(88);
});

it("handles flat tabular data without indicator column (uses column names as indicators)", () => {
it("uses global max across all indicators for relative comparison", () => {
const data = [
{ indicator: "ACTED_IN", value: 172 },
{ indicator: "PRODUCED", value: 15 },
{ indicator: "DIRECTED", value: 44 },
{ indicator: "WROTE", value: 10 },
{ indicator: "REVIEWED", value: 9 },
];
const result = transform(data) as { indicators: Array<{ name: string; max: number }>; series: Array<{ values: number[] }> };
// Global max: ceil(172 * 1.1) = 190
const globalMax = Math.ceil(172 * 1.1);
expect(result.indicators).toHaveLength(5);
// All indicators should share the same max
for (const ind of result.indicators) {
expect(ind.max).toBe(globalMax);
}
// The shape should NOT be uniform — values differ significantly
const values = result.series[0].values;
expect(values[0]).toBe(172); // ACTED_IN
expect(values[4]).toBe(9); // REVIEWED
});

it("preserves explicit max column values when provided", () => {
const data = [
{ indicator: "Speed", value: 80, max: 200 },
{ indicator: "Strength", value: 40, max: 150 },
];
const result = transform(data) as { indicators: Array<{ name: string; max: number }> };
expect(result.indicators[0].max).toBe(200);
expect(result.indicators[1].max).toBe(150);
});

it("uses global max for wide-format tabular data", () => {
const data = [{ Speed: 80, Strength: 60, Agility: 90 }];
const result = transform(data) as { indicators: Array<{ name: string }>; series: Array<{ values: number[] }> };
const result = transform(data) as { indicators: Array<{ name: string; max: number }>; series: Array<{ values: number[] }> };
// Global max: ceil(90 * 1.1) = 99
const globalMax = Math.ceil(90 * 1.1);
for (const ind of result.indicators) {
expect(ind.max).toBe(globalMax);
}
expect(result.indicators.map((i) => i.name)).toContain("Speed");
expect(result.indicators.map((i) => i.name)).toContain("Strength");
expect(result.series[0].values).toHaveLength(3);
Expand Down
15 changes: 9 additions & 6 deletions app/src/lib/chart-registry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -502,13 +502,15 @@ function transformToRadarData(data: unknown): unknown {
seriesMap.get(serName)!.set(indName, val);
}

// Use explicit max if provided, otherwise auto-scale from observed values (+10% headroom)
// Use explicit max if provided, otherwise use a single global max across all
// indicators so relative magnitudes are visible (e.g. 172 vs 9).
const indicatorEntries = Array.from(indicatorMaxFromData.keys());
const globalMax = Math.ceil(Math.max(...indicatorMaxFromData.values()) * 1.1) || 100;
const indicators = indicatorEntries.map((name) => ({
name,
max: maxKey && indicatorExplicitMax.has(name)
? indicatorExplicitMax.get(name)!
: Math.ceil((indicatorMaxFromData.get(name) ?? 100) * 1.1) || 100,
: globalMax,
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated
}));
const series = Array.from(seriesMap.entries()).map(([name, valMap]) => ({
name,
Expand All @@ -519,17 +521,18 @@ function transformToRadarData(data: unknown): unknown {
}

// Wide-format: each column is an indicator, each row is a series
// Auto-scale max from observed values per column (+10% headroom)
const maxPerCol = new Map<string, number>();
// Use a single global max so all axes share the same scale
let wideGlobalMax = 0;
for (const r of records) {
for (const k of keys) {
const v = Number(r[k]) || 0;
maxPerCol.set(k, Math.max(maxPerCol.get(k) ?? 0, v));
if (v > wideGlobalMax) wideGlobalMax = v;
}
}
const wideMax = Math.ceil(wideGlobalMax * 1.1) || 100;
const indicators = keys.map((k) => ({
name: k,
max: Math.ceil((maxPerCol.get(k) ?? 100) * 1.1) || 100,
max: wideMax,
}));
const series = records.map((r, i) => ({
name: String(i + 1),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,8 @@ vi.mock("@codemirror/commands", () => ({
vi.mock("@codemirror/autocomplete", () => ({
autocompletion: () => ({ type: "autocompletion" }),
completionKeymap: [],
closeBrackets: () => ({ type: "closeBrackets" }),
closeBracketsKeymap: [],
}));

vi.mock("@codemirror/theme-one-dark", () => ({
Expand Down
5 changes: 3 additions & 2 deletions component/src/components/composed/query-editor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ async function buildExtensions(
const [
{ EditorView, keymap, placeholder: cmPlaceholder },
{ defaultKeymap, historyKeymap, history: historyExt },
{ autocompletion, completionKeymap },
{ autocompletion, completionKeymap, closeBrackets, closeBracketsKeymap },
{ oneDark },
] = await Promise.all([
import("@codemirror/view"),
Expand Down Expand Up @@ -102,7 +102,8 @@ async function buildExtensions(

return [
historyExt(),
keymap.of([...defaultKeymap, ...historyKeymap, ...completionKeymap]),
closeBrackets(),
keymap.of([...defaultKeymap, ...historyKeymap, ...completionKeymap, ...closeBracketsKeymap]),
runKeymap,
langCompartmentExt,
autocompletion(),
Expand Down
Loading