feat(frontend): widget registry + universal fallback

This commit is contained in:
2026-06-28 12:22:37 +02:00
parent d238ddd395
commit e9aca179d7
4 changed files with 45 additions and 0 deletions
+14
View File
@@ -0,0 +1,14 @@
import { useState } from "react";
import type { WidgetProps } from "./types";
export function FallbackWidget({ descriptor, onRespond }: WidgetProps) {
const [text, setText] = useState("");
return (
<div className="border rounded p-3 space-y-2">
<p className="text-sm text-amber-600">Widget non supportato (kind: {descriptor.widget}) — rispondi manualmente</p>
<pre className="text-xs overflow-auto max-h-48 bg-muted p-2">{JSON.stringify(descriptor, null, 2)}</pre>
<textarea className="w-full border rounded p-1" value={text} onChange={(e) => setText(e.target.value)} />
<button className="border rounded px-2 py-1" onClick={() => onRespond({ id: descriptor.id, control: "freetext", text })}>Invia</button>
</div>
);
}
+15
View File
@@ -0,0 +1,15 @@
import { render, screen } from "@testing-library/react";
import { register, resolve } from "./registry";
import type { WidgetProps } from "./types";
test("resolve returns the registered renderer", () => {
const Dummy = (_: WidgetProps) => <div>dummy</div>;
register("dummy", Dummy);
expect(resolve("dummy")).toBe(Dummy);
});
test("resolve falls back for unknown kind and shows the JSON", () => {
const Comp = resolve("totally-unknown");
render(<Comp descriptor={{ id: "u1", widget: "totally-unknown", title: "X" } as any} onRespond={() => {}} />);
expect(screen.getByText(/Widget non supportato/i)).toBeInTheDocument();
});
+13
View File
@@ -0,0 +1,13 @@
import type React from "react";
import type { WidgetProps } from "./types";
import { FallbackWidget } from "./FallbackWidget";
const registry = new Map<string, React.FC<WidgetProps>>();
export function register(kind: string, comp: React.FC<WidgetProps>) {
registry.set(kind, comp);
}
export function resolve(kind: string): React.FC<WidgetProps> {
return registry.get(kind) ?? FallbackWidget;
}
+3
View File
@@ -0,0 +1,3 @@
import type { WidgetDescriptor, UiResponse } from "../api/types";
export type WidgetProps = { descriptor: WidgetDescriptor; onRespond: (r: UiResponse) => void };