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";
|
||||
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>
|
||||
|
||||
Reference in New Issue
Block a user