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:
Developer
2026-07-06 10:43:12 +00:00
parent bfe7ce7367
commit f355d04278
+38 -10
View File
@@ -7,6 +7,7 @@ import {
} from "@/components/ui/dialog";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Textarea } from "@/components/ui/textarea";
import { Label } from "@/components/ui/label";
import { Switch } from "@/components/ui/switch";
import {
@@ -133,17 +134,35 @@ function WidgetConfigEditor({
return (
<div className="flex flex-col gap-3">
{properties.map(([key, schema]) => {
{properties.map(([key, schema]) => {
const isNumber =
(schema as { type?: string }).type === "integer" ||
(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 (
<Field
key={key}
label={key}
htmlFor={`widget-cfg-${key}`}
helper={(schema as { description?: string }).description}
>
<Field
key={key}
label={key}
htmlFor={`widget-cfg-${key}`}
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
id={`widget-cfg-${key}`}
type={isNumber ? "number" : "text"}
@@ -159,7 +178,8 @@ function WidgetConfigEditor({
})
}
/>
</Field>
)}
</Field>
);
})}
</div>
@@ -334,10 +354,18 @@ export function WidgetConfigDialog({ open, onClose, serviceId }: Props) {
/>
{!isMobile ? (
<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
</Button>
<Button onClick={saveDraft} disabled={saveWidget.isPending} className="mobile-touch-target">
<Button
onClick={saveDraft}
disabled={saveWidget.isPending}
className="mobile-touch-target"
>
Save widget
</Button>
</div>