Use multi-line textarea for complex widget config fields
The Grafana chart widget's 'query' field (PromQL) and any schema field marked format:'textarea' now render as a resizable 4-row Textarea with monospace font, instead of a single-line Input. Makes complex queries much easier to read and edit. The field-renderer heuristic: key === 'query' or schema format === 'textarea' → Textarea; everything else stays an Input (numbers stay number inputs). 127 tests pass; lint/build green.
This commit is contained in:
@@ -7,6 +7,7 @@ import {
|
|||||||
} from "@/components/ui/dialog";
|
} from "@/components/ui/dialog";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { Input } from "@/components/ui/input";
|
import { Input } from "@/components/ui/input";
|
||||||
|
import { Textarea } from "@/components/ui/textarea";
|
||||||
import { Label } from "@/components/ui/label";
|
import { Label } from "@/components/ui/label";
|
||||||
import { Switch } from "@/components/ui/switch";
|
import { Switch } from "@/components/ui/switch";
|
||||||
import {
|
import {
|
||||||
@@ -133,17 +134,35 @@ function WidgetConfigEditor({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col gap-3">
|
<div className="flex flex-col gap-3">
|
||||||
{properties.map(([key, schema]) => {
|
{properties.map(([key, schema]) => {
|
||||||
const isNumber =
|
const isNumber =
|
||||||
(schema as { type?: string }).type === "integer" ||
|
(schema as { type?: string }).type === "integer" ||
|
||||||
(schema as { type?: string }).type === "number";
|
(schema as { type?: string }).type === "number";
|
||||||
|
// Use a multi-line textarea for fields that tend to hold complex
|
||||||
|
// multi-line values (PromQL, text blocks, etc.). The widget kind's
|
||||||
|
// config schema can opt in via `format: "textarea"`; the well-known
|
||||||
|
// `query` field is treated as textarea by default.
|
||||||
|
const schemaFormat = (schema as { format?: string }).format;
|
||||||
|
const isTextarea =
|
||||||
|
schemaFormat === "textarea" || key === "query";
|
||||||
return (
|
return (
|
||||||
<Field
|
<Field
|
||||||
key={key}
|
key={key}
|
||||||
label={key}
|
label={key}
|
||||||
htmlFor={`widget-cfg-${key}`}
|
htmlFor={`widget-cfg-${key}`}
|
||||||
helper={(schema as { description?: string }).description}
|
helper={(schema as { description?: string }).description}
|
||||||
>
|
>
|
||||||
|
{isTextarea ? (
|
||||||
|
<Textarea
|
||||||
|
id={`widget-cfg-${key}`}
|
||||||
|
rows={4}
|
||||||
|
className="resize-y font-mono text-xs"
|
||||||
|
value={String(config[key] ?? "")}
|
||||||
|
onChange={(e) =>
|
||||||
|
onChange({ ...config, [key]: e.target.value })
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
<Input
|
<Input
|
||||||
id={`widget-cfg-${key}`}
|
id={`widget-cfg-${key}`}
|
||||||
type={isNumber ? "number" : "text"}
|
type={isNumber ? "number" : "text"}
|
||||||
@@ -159,7 +178,8 @@ function WidgetConfigEditor({
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
</Field>
|
)}
|
||||||
|
</Field>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
@@ -334,10 +354,18 @@ export function WidgetConfigDialog({ open, onClose, serviceId }: Props) {
|
|||||||
/>
|
/>
|
||||||
{!isMobile ? (
|
{!isMobile ? (
|
||||||
<div className="flex justify-end gap-2">
|
<div className="flex justify-end gap-2">
|
||||||
<Button variant="outline" onClick={reset} className="mobile-touch-target">
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
onClick={reset}
|
||||||
|
className="mobile-touch-target"
|
||||||
|
>
|
||||||
Back
|
Back
|
||||||
</Button>
|
</Button>
|
||||||
<Button onClick={saveDraft} disabled={saveWidget.isPending} className="mobile-touch-target">
|
<Button
|
||||||
|
onClick={saveDraft}
|
||||||
|
disabled={saveWidget.isPending}
|
||||||
|
className="mobile-touch-target"
|
||||||
|
>
|
||||||
Save widget
|
Save widget
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user