feat(editor): NodeEditorDialog exposes PinnedDataSection per node category
This commit is contained in:
@@ -249,4 +249,31 @@ describe("NodeEditorDialog", () => {
|
||||
await userEvent.click(await screen.findByText("email"));
|
||||
expect(body.value).toBe("{input.email}");
|
||||
});
|
||||
|
||||
it("action nodes show a 'Pinned input' section", () => {
|
||||
renderDialog();
|
||||
expect(screen.getByText(/pinned input/i)).toBeInTheDocument();
|
||||
expect(screen.queryByText(/pinned output/i)).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("trigger nodes show a 'Pinned output' section instead", () => {
|
||||
const node: Node<VcNodeData> = {
|
||||
id: "w1", type: "vc",
|
||||
position: { x: 0, y: 0 },
|
||||
data: { typeId: "http.webhook", name: "Webhook", config: { path: "hook" }, connection_id: null },
|
||||
};
|
||||
renderDialog({ node });
|
||||
expect(screen.getByText(/pinned output/i)).toBeInTheDocument();
|
||||
expect(screen.queryByText(/pinned input/i)).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("editing pin JSON flows into node.data.pinnedInput", async () => {
|
||||
renderControlledDialog();
|
||||
// The Pinned input textarea has aria-label "Pinned input".
|
||||
const ta = screen.getByLabelText(/^pinned input$/i) as HTMLTextAreaElement;
|
||||
await userEvent.type(ta, `{{"a":1}`); // userEvent's {{ escapes to {
|
||||
// Ends with `{"a":1}` — the textarea value should reflect it.
|
||||
// Harness applies onPatch, so the controlled value stays synced.
|
||||
expect(ta.value).toContain(`"a":1`);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -10,6 +10,7 @@ import { JsonTree } from "@/expr/JsonTree";
|
||||
import { findLatestIo } from "./nodeIoLookup";
|
||||
import type { VcNodeData } from "./graphAdapter";
|
||||
import type { ExpressionFieldHandle } from "@/expr/ExpressionField";
|
||||
import { PinnedDataSection } from "./PinnedDataSection";
|
||||
|
||||
interface Props {
|
||||
node: Node<VcNodeData>;
|
||||
@@ -188,6 +189,22 @@ export function NodeEditorDialog({
|
||||
onFieldFocus={registerFocus}
|
||||
/>
|
||||
)}
|
||||
{nt && (nt.inputCount > 0 || nt.outputCount > 0) && (
|
||||
<PinnedDataSection
|
||||
title={nt.category === "Trigger" ? "Pinned output" : "Pinned input"}
|
||||
workflowId={workflowId}
|
||||
nodeId={node.id}
|
||||
side={nt.category === "Trigger" ? "outputs" : "inputs"}
|
||||
value={nt.category === "Trigger" ? (node.data.pinnedOutput ?? null) : (node.data.pinnedInput ?? null)}
|
||||
onChange={(v) =>
|
||||
onPatch(
|
||||
nt.category === "Trigger"
|
||||
? { pinnedOutput: v }
|
||||
: { pinnedInput: v },
|
||||
)
|
||||
}
|
||||
/>
|
||||
)}
|
||||
{node.data.typeId === "http.webhook" && (
|
||||
<WebhookUrlHint
|
||||
workflowId={workflowId}
|
||||
|
||||
Reference in New Issue
Block a user