From f2e33c69bcf925fe9f0b9995b9acedbdae3bc8f6 Mon Sep 17 00:00:00 2001 From: Jonathan Dobson Date: Fri, 15 Aug 2025 08:53:29 -0400 Subject: [PATCH] fix accordion rebder bug from flippable component (#3203) --- skyvern-frontend/src/hooks/useRerender.ts | 34 +++++++++++++++++++ .../editor/nodes/ActionNode/ActionNode.tsx | 5 ++- .../nodes/ExtractionNode/ExtractionNode.tsx | 10 ++++-- .../FileDownloadNode/FileDownloadNode.tsx | 10 ++++-- .../nodes/HttpRequestNode/HttpRequestNode.tsx | 10 ++++-- .../editor/nodes/LoginNode/LoginNode.tsx | 10 ++++-- .../nodes/NavigationNode/NavigationNode.tsx | 5 ++- .../editor/nodes/TaskNode/TaskNode.tsx | 11 ++++-- .../editor/nodes/Taskv2Node/Taskv2Node.tsx | 10 ++++-- .../nodes/ValidationNode/ValidationNode.tsx | 11 ++++-- 10 files changed, 100 insertions(+), 16 deletions(-) create mode 100644 skyvern-frontend/src/hooks/useRerender.ts diff --git a/skyvern-frontend/src/hooks/useRerender.ts b/skyvern-frontend/src/hooks/useRerender.ts new file mode 100644 index 00000000..45a4f59b --- /dev/null +++ b/skyvern-frontend/src/hooks/useRerender.ts @@ -0,0 +1,34 @@ +import { useState } from "react"; + +/** + * ```tsx + * const { bump, key } = useRerender({ delay: 40,prefix: "my-prefix" }); + * + *
...
+ * + * // somewhere else + * bump(); + * ``` + */ +const useRerender = ({ + delay = 40, + prefix, +}: { + delay?: number; + prefix: string; +}) => { + const [forceRenderKey, setForceRenderKey] = useState(`${prefix}-0`); + + const bump = () => { + setTimeout(() => { + setForceRenderKey((prev) => `${prefix}-${prev + 1}`); + }, delay); + }; + + return { + bump, + key: forceRenderKey, + }; +}; + +export { useRerender }; diff --git a/skyvern-frontend/src/routes/workflows/editor/nodes/ActionNode/ActionNode.tsx b/skyvern-frontend/src/routes/workflows/editor/nodes/ActionNode/ActionNode.tsx index 0945fbed..71246f05 100644 --- a/skyvern-frontend/src/routes/workflows/editor/nodes/ActionNode/ActionNode.tsx +++ b/skyvern-frontend/src/routes/workflows/editor/nodes/ActionNode/ActionNode.tsx @@ -25,6 +25,7 @@ import { CodeEditor } from "@/routes/workflows/components/CodeEditor"; import { Switch } from "@/components/ui/switch"; import { placeholders, helpTooltips } from "../../helpContent"; import { WorkflowBlockInputTextarea } from "@/components/WorkflowBlockInputTextarea"; +import { useRerender } from "@/hooks/useRerender"; import { BlockCodeEditor } from "@/routes/workflows/components/BlockCodeEditor"; import { WorkflowBlockInput } from "@/components/WorkflowBlockInput"; import { AppNode } from ".."; @@ -77,6 +78,7 @@ function ActionNode({ id, data, type }: NodeProps) { const thisBlockIsPlaying = workflowRunIsRunningOrQueued && thisBlockIsTargetted; const elideFromDebugging = debugStore.isDebugMode && !debuggable; + const rerender = useRerender({ prefix: "accordian" }); const nodes = useNodes(); const edges = useEdges(); @@ -189,6 +191,7 @@ function ActionNode({ id, data, type }: NodeProps) { "pointer-events-none opacity-50": thisBlockIsPlaying, })} type="single" + onValueChange={() => rerender.bump()} collapsible > @@ -196,7 +199,7 @@ function ActionNode({ id, data, type }: NodeProps) { Advanced Settings -
+
) { const { updateNodeData } = useReactFlow(); @@ -70,6 +71,7 @@ function ExtractionNode({ id, data, type }: NodeProps) { const nodes = useNodes(); const edges = useEdges(); const outputParameterKeys = getAvailableOutputParameterKeys(nodes, edges, id); + const rerender = useRerender({ prefix: "accordian" }); const isFirstWorkflowBlock = useIsFirstBlockInWorkflow({ id }); @@ -161,13 +163,17 @@ function ExtractionNode({ id, data, type }: NodeProps) { }} /> - + rerender.bump()} + > Advanced Settings -
+
) { engine: data.engine, model: data.model, }); + const rerender = useRerender({ prefix: "accordian" }); const nodes = useNodes(); const edges = useEdges(); const outputParameterKeys = getAvailableOutputParameterKeys(nodes, edges, id); @@ -171,13 +173,17 @@ function FileDownloadNode({ id, data }: NodeProps) {
- + rerender.bump()} + > Advanced Settings -
+
) { }); const deleteNodeCallback = useDeleteNodeCallback(); + const rerender = useRerender({ prefix: "accordian" }); const nodes = useNodes(); const edges = useEdges(); const outputParameterKeys = getAvailableOutputParameterKeys(nodes, edges, id); @@ -319,12 +321,16 @@ function HttpRequestNode({ id, data }: NodeProps) { - + rerender.bump()} + > Advanced Settings - +
) { const { updateNodeData } = useReactFlow(); @@ -73,6 +74,7 @@ function LoginNode({ id, data, type }: NodeProps) { model: data.model, }); + const rerender = useRerender({ prefix: "accordian" }); const nodes = useNodes(); const edges = useEdges(); const outputParameterKeys = getAvailableOutputParameterKeys(nodes, edges, id); @@ -185,12 +187,16 @@ function LoginNode({ id, data, type }: NodeProps) {
- + rerender.bump()} + > Advanced Settings - +
) { const thisBlockIsPlaying = workflowRunIsRunningOrQueued && thisBlockIsTargetted; const elideFromDebugging = debugStore.isDebugMode && !debuggable; + const rerender = useRerender({ prefix: "accordian" }); const [inputs, setInputs] = useState({ allowDownloads: data.allowDownloads, cacheActions: data.cacheActions, @@ -194,13 +196,14 @@ function NavigationNode({ id, data, type }: NodeProps) { })} type="single" collapsible + onValueChange={() => rerender.bump()} > Advanced Settings -
+
) { const { updateNodeData } = useReactFlow(); @@ -59,6 +60,8 @@ function TaskNode({ id, data, type }: NodeProps) { urlBlockLabel !== undefined && urlBlockLabel === label; const thisBlockIsPlaying = workflowRunIsRunningOrQueued && thisBlockIsTargetted; + + const rerender = useRerender({ prefix: "accordian" }); const nodes = useNodes(); const edges = useEdges(); const outputParameterKeys = getAvailableOutputParameterKeys(nodes, edges, id); @@ -130,10 +133,14 @@ function TaskNode({ id, data, type }: NodeProps) { totpUrl={inputs.totpVerificationUrl} type={type} /> - + rerender.bump()} + > Content - +
diff --git a/skyvern-frontend/src/routes/workflows/editor/nodes/Taskv2Node/Taskv2Node.tsx b/skyvern-frontend/src/routes/workflows/editor/nodes/Taskv2Node/Taskv2Node.tsx index 58cd0d8b..9b61ea62 100644 --- a/skyvern-frontend/src/routes/workflows/editor/nodes/Taskv2Node/Taskv2Node.tsx +++ b/skyvern-frontend/src/routes/workflows/editor/nodes/Taskv2Node/Taskv2Node.tsx @@ -21,6 +21,7 @@ import { NodeHeader } from "../components/NodeHeader"; import { useParams } from "react-router-dom"; import { statusIsRunningOrQueued } from "@/routes/tasks/types"; import { useWorkflowRunQuery } from "@/routes/workflows/hooks/useWorkflowRunQuery"; +import { useRerender } from "@/hooks/useRerender"; function Taskv2Node({ id, data, type }: NodeProps) { const { debuggable, editable, label } = data; @@ -36,6 +37,7 @@ function Taskv2Node({ id, data, type }: NodeProps) { workflowRunIsRunningOrQueued && thisBlockIsTargetted; const { updateNodeData } = useReactFlow(); const isFirstWorkflowBlock = useIsFirstBlockInWorkflow({ id }); + const rerender = useRerender({ prefix: "accordian" }); const [inputs, setInputs] = useState({ prompt: data.prompt, @@ -122,12 +124,16 @@ function Taskv2Node({ id, data, type }: NodeProps) {
- + rerender.bump()} + > Advanced Settings - +
) { const { updateNodeData } = useReactFlow(); @@ -61,6 +62,8 @@ function ValidationNode({ id, data, type }: NodeProps) { errorCodeMapping: data.errorCodeMapping, model: data.model, }); + + const rerender = useRerender({ prefix: "accordian" }); const nodes = useNodes(); const edges = useEdges(); const outputParameterKeys = getAvailableOutputParameterKeys(nodes, edges, id); @@ -143,13 +146,17 @@ function ValidationNode({ id, data, type }: NodeProps) { />
- + rerender.bump()} + > Advanced Settings -
+