fix: widget edit crash (#185) + resizable textarea for complex fields

WidgetConfigDialog crashed on edit with React error #185 (Maximum update
depth exceeded) when the references/instances query returned undefined and
the inline '= []' fallback created a new array ref every render, looping the
auto-edit useEffect. Stabilize via useMemo(data ?? []). Also moved the
referencedWidgetIds Set inside the availableWidgets useMemo (clears the
pre-existing exhaustive-deps warning).

Complex config fields (promql, query, text, command, notes, or opt-in via
format: 'textarea') now render as a taller resizable Textarea (rows=6,
min-h-120px, font-mono, resize) in both WidgetConfigFields and
ServiceConfigFields, instead of a single-line Input.

Build + lint clean (referencedWidgetIds warning gone), 165 vitest pass.
This commit is contained in:
Developer
2026-07-11 10:31:57 +00:00
parent 044d386ac7
commit ecabc65dd4
2 changed files with 53 additions and 25 deletions
+23 -8
View File
@@ -5,6 +5,7 @@ import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { Textarea } from "@/components/ui/textarea";
import { Switch } from "@/components/ui/switch";
import {
Dialog,
@@ -99,20 +100,33 @@ function ServiceConfigFields({
const properties =
(
type.config_schema as {
properties?: Record<string, { type?: string; description?: string }>;
properties?: Record<string, { type?: string; description?: string; format?: string }>;
}
).properties ?? {};
// Multi-line resizable textarea for fields that hold complex values (opt-in
// via `format: "textarea"`, or well-known multi-line keys).
const TEXTAREA_KEYS = new Set(["notes", "command"]);
return (
<div className="flex flex-col gap-3">
{Object.entries(properties).map(([key, schema]) => {
const isNumber = schema.type === "integer" || schema.type === "number";
const isTextarea = schema.format === "textarea" || TEXTAREA_KEYS.has(key);
return (
<Field
key={key}
label={key}
htmlFor={`cfg-${key}`}
helper={schema.description}
>
<Field
key={key}
label={key}
htmlFor={`cfg-${key}`}
helper={schema.description}
>
{isTextarea ? (
<Textarea
id={`cfg-${key}`}
rows={6}
className="resize font-mono text-xs min-h-[120px]"
value={String(config[key] ?? "")}
onChange={(e) => onChange({ ...config, [key]: e.target.value })}
/>
) : (
<Input
id={`cfg-${key}`}
type={isNumber ? "number" : "text"}
@@ -128,7 +142,8 @@ function ServiceConfigFields({
})
}
/>
</Field>
)}
</Field>
);
})}
</div>