refactor: update ReactFlow to v12 (#5317)

* Added xyflow and updated imports

* Fix changing node.width to node.measured.width

* Updated NodeType to follow new API

* Fixed note node data type

* Created AllNodeType to contain note node type

* Changed flow types to follow new type from reactflow 12

* Updated flowStore to work with new types

* Updated flowStore and reactFlowUtils to work with custom edge type

* Updated updateAllNodes to use new type

* Updated PageComponent to follow new names for node dragging and edge reconnect

* Made selected prop be optional

* Changed reactFlowInstance to have nodes and edges type

* Updated updateAllComponent to follow new types

* Updated ReactFlowUtils with new types

* Updated reactFlowUtils type

* Updated all reactFlowUtils to be generic

* Updated handleRenderComponent with Connection type

* Updated node description and name with null checks for names

* Added check if node is genericNode on nodeOutputField

* Updated note node type

* Updated nodestatus with selected null check

* Updated notenode with new node type

* Update NodeType imports to be AllNodeType

* Fix more lint issues

* Fixed react flow button css

* ✨ (freeze.spec.ts): add zoomOut utility function to handle zooming out in tests for better code reusability and readability
📝 (decisionFlow.spec.ts): import zoomOut utility function to handle zooming out in tests for better code reusability and readability
📝 (zoom-out.ts): create zoomOut utility function to handle zooming out in tests for better code reusability and readability

* 🐛 (generalBugs-shard-10.spec.ts): fix test to wait for the element with class "border-ring-frozen" to be visible before asserting its count

* 📝 (generalBugs-shard-10.spec.ts): remove unnecessary locator click on element with id 'react-flow-id' to improve test reliability and maintainability

* 📝 (SelectionMenuComponent): add data-testid attribute to differentiate error-group-node and group-node components
📝 (similarity.spec.ts): refactor dragTo calls to use targetPosition for better accuracy, replace zoom_out clicks with zoomOut function, updateOldComponents function to handle outdated components
📝 (generalBugs-shard-5.spec.ts): replace zoom_out clicks with zoomOut function, add waitForSelector for group-node before clicking, remove unnecessary mouse interactions
📝 (intComponent.spec.ts): replace zoom_out clicks with zoomOut function, click on div-generic-node component
📝 (keyPairListComponent.spec.ts): click on div-generic-node component, adjustScreenView function call
📝 (nestedComponent.spec.ts): remove unnecessary click on react-flow-id element
📝 (generalBugs-shard-7.spec.ts): replace zoom_out clicks with zoomOut function

* ✨ (stop-building.spec.ts): Add new utility functions to improve code modularity and readability
🔧 (stop-building.spec.ts): Refactor drag and drop operations to use utility functions for better maintainability
🔧 (stop-building.spec.ts): Refactor zoom out operations to use utility function for consistency
🔧 (stop-building.spec.ts): Refactor component positioning operations to use utility functions for clarity
🔧 (stop-building.spec.ts): Refactor outdated components and filled API keys handling to use utility functions for reusability
🔧 (stop-building.spec.ts): Refactor fit view operation to use utility function for consistency

* ✨ (generalBugs-shard-9.spec.ts): refactor test to use initialGPTsetup function for GPT setup instead of manual steps to improve code readability and maintainability

* ✨ (store-shard-2.spec.ts): Increase timeout for clicking "api-key-button-store" to 200000ms for better test stability
✨ (deleteComponents.spec.ts, deleteFlows.spec.ts, store-shard-1.spec.ts, store-shard-3.spec.ts): Increase timeout for clicking "api-key-button-store" to 200000ms for better test stability
✨ (store-shard-1.spec.ts, store-shard-3.spec.ts): Remove unnecessary waitForSelector and add timeout of 200000ms for clicking "api-key-button-store" for better test stability

* ✨ (fileUploadComponent.spec.ts): update dragTo method calls with targetPosition option to specify the position of the drag action

* ✨ (decisionFlow.spec.ts): add explicit wait for an element to be attached before interacting with it to improve test reliability
🐛 (sticky-notes.spec.ts): fix test by adding keyboard press to simulate pressing the Escape key to close a modal before checking text length

* ✅ (sticky-notes.spec.ts): update assertion to use 'toHaveCount' matcher for improved test readability and reliability

* Added function to make controls horizontal

* ✨ (sticky-notes.spec.ts): update selector for textMarkdown to improve test reliability and readability

---------

Co-authored-by: cristhianzl <cristhian.lousa@gmail.com>
This commit is contained in:
Lucas Oliveira 2024-12-18 16:40:13 -03:00 • committed by GitHub
commit 7d6d41aa87
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
74 changed files with 685 additions and 759 deletions

View file

@ -36,6 +36,7 @@
"@tailwindcss/line-clamp": "^0.4.4",
"@tanstack/react-query": "^5.49.2",
"@types/axios": "^0.14.0",
"@xyflow/react": "^12.3.6",
"ace-builds": "^1.35.0",
"ag-grid-community": "^32.0.2",
"ag-grid-react": "^32.0.2",
@ -6238,6 +6239,36 @@
"integrity": "sha512-b5o1I6aLNeYlU/3CPlj/Z91ybk1gUsKT+5NAJI+2W4UjvS5KLG28K9v5UvNoFVjHV8PajVZ00RH3vnjyQO7ZAw==",
"license": "MIT"
},
"node_modules/@xyflow/react": {
"version": "12.3.6",
"resolved": "https://registry.npmjs.org/@xyflow/react/-/react-12.3.6.tgz",
"integrity": "sha512-9GS+cz8hDZahpvTrVCmySAEgKUL8oN4b2q1DluHrKtkqhAMWfH2s7kblhbM4Y4Y4SUnH2lt4drXKZ/4/Lot/2Q==",
"license": "MIT",
"dependencies": {
"@xyflow/system": "0.0.47",
"classcat": "^5.0.3",
"zustand": "^4.4.0"
},
"peerDependencies": {
"react": ">=17",
"react-dom": ">=17"
}
},
"node_modules/@xyflow/system": {
"version": "0.0.47",
"resolved": "https://registry.npmjs.org/@xyflow/system/-/system-0.0.47.tgz",
"integrity": "sha512-aUXJPIvsCFxGX70ccRG8LPsR+A8ExYXfh/noYNpqn8udKerrLdSHxMG2VsvUrQ1PGex10fOpbJwFU4A+I/Xv8w==",
"license": "MIT",
"dependencies": {
"@types/d3-drag": "^3.0.7",
"@types/d3-selection": "^3.0.10",
"@types/d3-transition": "^3.0.8",
"@types/d3-zoom": "^3.0.8",
"d3-drag": "^3.0.0",
"d3-selection": "^3.0.0",
"d3-zoom": "^3.0.0"
}
},
"node_modules/abab": {
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz",

View file

@ -31,6 +31,7 @@
"@tailwindcss/line-clamp": "^0.4.4",
"@tanstack/react-query": "^5.49.2",
"@types/axios": "^0.14.0",
"@xyflow/react": "^12.3.6",
"ace-builds": "^1.35.0",
"ag-grid-community": "^32.0.2",
"ag-grid-react": "^32.0.2",

View file

@ -137,6 +137,9 @@ body {
stroke: var(--selected) !important;
stroke-width: 2px !important;
}
.react-flow__controls-button svg {
fill: none !important;
}
.react-flow__edge .react-flow__edge-path {
transition: color;

View file

@ -1,6 +1,6 @@
import "@xyflow/react/dist/style.css";
import { Suspense } from "react";
import { RouterProvider } from "react-router-dom";
import "reactflow/dist/style.css";
import { LoadingPage } from "./pages/LoadingPage";
import router from "./routes";

View file

@ -1,5 +1,5 @@
import useFlowStore from "@/stores/flowStore";
import { BaseEdge, EdgeProps, getBezierPath, Position } from "reactflow";
import { BaseEdge, EdgeProps, getBezierPath, Position } from "@xyflow/react";
export function DefaultEdge({
sourceHandleId,
@ -17,7 +17,8 @@ export function DefaultEdge({
const sourceNode = getNode(source);
const targetNode = getNode(target);
const sourceXNew = (sourceNode?.position.x ?? 0) + (sourceNode?.width ?? 0);
const sourceXNew =
(sourceNode?.position.x ?? 0) + (sourceNode?.measured?.width ?? 0);
const targetXNew = targetNode?.position.x ?? 0;
const [edgePath] = getBezierPath({

View file

@ -18,7 +18,7 @@ export default function NodeDescription({
style,
}: {
description?: string;
selected: boolean;
selected?: boolean;
nodeId: string;
emptyPlaceholder?: string;
placeholderClassName?: string;
@ -28,7 +28,9 @@ export default function NodeDescription({
style?: React.CSSProperties;
}) {
const [inputDescription, setInputDescription] = useState(false);
const [nodeDescription, setNodeDescription] = useState(description);
const [nodeDescription, setNodeDescription] = useState<string>(
description ?? "",
);
const takeSnapshot = useFlowsManagerStore((state) => state.takeSnapshot);
const setNode = useFlowStore((state) => state.setNode);
const overflowRef = useRef<HTMLDivElement>(null);
@ -56,7 +58,7 @@ export default function NodeDescription({
}, [selected]);
useEffect(() => {
setNodeDescription(description);
setNodeDescription(description ?? "");
}, [description]);
const MemoizedMarkdown = memo(Markdown);

View file

@ -15,7 +15,7 @@ export default function NodeName({
beta,
}: {
display_name?: string;
selected: boolean;
selected?: boolean;
nodeId: string;
showNode: boolean;
validationStatus: VertexBuildTypeAPI | null;
@ -23,7 +23,7 @@ export default function NodeName({
beta: boolean;
}) {
const [inputName, setInputName] = useState(false);
const [nodeName, setNodeName] = useState(display_name);
const [nodeName, setNodeName] = useState<string>(display_name ?? "");
const takeSnapshot = useFlowsManagerStore((state) => state.takeSnapshot);
const setNode = useFlowStore((state) => state.setNode);
useEffect(() => {
@ -33,7 +33,7 @@ export default function NodeName({
}, [selected]);
useEffect(() => {
setNodeName(display_name);
setNodeName(display_name ?? "");
}, [display_name]);
return inputName ? (
@ -54,7 +54,7 @@ export default function NodeName({
},
}));
} else {
setNodeName(display_name);
setNodeName(display_name ?? "");
}
}}
value={nodeName}

View file

@ -1,7 +1,7 @@
import { ICON_STROKE_WIDTH } from "@/constants/constants";
import { useUpdateNodeInternals } from "@xyflow/react";
import { cloneDeep } from "lodash";
import { memo, useCallback, useEffect, useMemo, useRef } from "react";
import { useUpdateNodeInternals } from "reactflow";
import { default as IconComponent } from "../../../../components/common/genericIconComponent";
import ShadTooltip from "../../../../components/common/shadTooltipComponent";
import { Button } from "../../../../components/ui/button";
@ -201,7 +201,8 @@ function NodeOutputField({
const handleUpdateOutputHide = useCallback(
(value?: boolean) => {
setNode(data.id, (oldNode) => {
const newNode = cloneDeep(oldNode);
if (oldNode.type !== "genericNode") return oldNode;
let newNode = cloneDeep(oldNode);
newNode.data = {
...newNode.data,
node: {

View file

@ -5,13 +5,7 @@ import useValidationStatusString from "@/CustomNodes/hooks/use-validation-status
import ShadTooltip from "@/components/common/shadTooltipComponent";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import {
ICON_STROKE_WIDTH,
RUN_TIMESTAMP_PREFIX,
STATUS_BUILD,
STATUS_BUILDING,
STATUS_INACTIVE,
} from "@/constants/constants";
import { ICON_STROKE_WIDTH } from "@/constants/constants";
import { BuildStatus } from "@/constants/enums";
import { track } from "@/customization/utils/analytics";
import { useDarkStore } from "@/stores/darkStore";
@ -43,7 +37,7 @@ export default function NodeStatus({
}: {
nodeId: string;
display_name: string;
selected: boolean;
selected?: boolean;
setBorderColor: (color: string) => void;
frozen?: boolean;
showNode: boolean;
@ -101,8 +95,7 @@ export default function NodeStatus({
return cn(frozen ? frozenClass : className, updateClass);
};
const getNodeBorderClassName = (
selected: boolean,
showNode: boolean,
selected: boolean | undefined,
buildStatus: BuildStatus | undefined,
validationStatus: VertexBuildTypeAPI | null,
) => {
@ -119,7 +112,7 @@ export default function NodeStatus({
useEffect(() => {
setBorderColor(
getNodeBorderClassName(selected, showNode, buildStatus, validationStatus),
getNodeBorderClassName(selected, buildStatus, validationStatus),
);
}, [
selected,

View file

@ -1,7 +1,7 @@
import { useDarkStore } from "@/stores/darkStore";
import useFlowStore from "@/stores/flowStore";
import { Connection, Handle, Position } from "@xyflow/react";
import { memo, useCallback, useEffect, useMemo, useRef, useState } from "react";
import { Handle, Position } from "reactflow";
import ShadTooltip from "../../../../components/common/shadTooltipComponent";
import {
isValidConnection,
@ -413,7 +413,9 @@ const HandleRenderComponent = memo(function HandleRenderComponent({
type={left ? "target" : "source"}
position={left ? Position.Left : Position.Right}
id={myId}
isValidConnection={validateConnection}
isValidConnection={(connection) =>
isValidConnection(connection as Connection, nodes, edges)
}
className={cn(
`group/handle z-50 transition-all`,
!showNode && "no-show",

View file

@ -1,9 +1,9 @@
import ForwardedIconComponent from "@/components/common/genericIconComponent";
import ShadTooltip from "@/components/common/shadTooltipComponent";
import { usePostValidateComponentCode } from "@/controllers/API/queries/nodes/use-post-validate-component-code";
import { useUpdateNodeInternals } from "@xyflow/react";
import { memo, useCallback, useEffect, useMemo, useState } from "react";
import { useHotkeys } from "react-hotkeys-hook";
import { useUpdateNodeInternals } from "reactflow";
import { Button } from "../../components/ui/button";
import {
TOOLTIP_HIDDEN_OUTPUTS,
@ -65,7 +65,7 @@ function GenericNode({
selected,
}: {
data: NodeDataType;
selected: boolean;
selected?: boolean;
xPos?: number;
yPos?: number;
}): JSX.Element {

View file

@ -10,7 +10,7 @@ import useAlertStore from "@/stores/alertStore";
import useFlowStore from "@/stores/flowStore";
import useFlowsManagerStore from "@/stores/flowsManagerStore";
import { useShortcutsStore } from "@/stores/shortcuts";
import { noteDataType } from "@/types/flow";
import { NoteDataType } from "@/types/flow";
import { classNames, cn, openInNewTab } from "@/utils/utils";
import { cloneDeep } from "lodash";
import { memo, useCallback, useMemo } from "react";
@ -22,7 +22,7 @@ const NoteToolbarComponent = memo(function NoteToolbarComponent({
data,
bgColor,
}: {
data: noteDataType;
data: NoteDataType;
bgColor: string;
}) {
const setNoticeData = useAlertStore((state) => state.setNoticeData);
@ -69,21 +69,18 @@ const NoteToolbarComponent = memo(function NoteToolbarComponent({
setLastCopiedSelection({ nodes: cloneDeep(node), edges: [] });
break;
case "duplicate":
const targetNode = nodes.find((node) => node.id === data.id);
if (targetNode) {
paste(
{
nodes: [targetNode],
edges: [],
},
{
x: 50,
y: 10,
paneX: targetNode.position.x,
paneY: targetNode.position.y,
},
);
}
paste(
{
nodes: [nodes.find((node) => node.id === data.id)!],
edges: [],
},
{
x: 50,
y: 10,
paneX: nodes.find((node) => node.id === data.id)?.position.x,
paneY: nodes.find((node) => node.id === data.id)?.position.y,
},
);
break;
}
},

View file

@ -5,18 +5,18 @@ import {
NOTE_NODE_MIN_HEIGHT,
NOTE_NODE_MIN_WIDTH,
} from "@/constants/constants";
import { noteDataType } from "@/types/flow";
import { NoteDataType } from "@/types/flow";
import { cn } from "@/utils/utils";
import { NodeResizer } from "@xyflow/react";
import { useEffect, useMemo, useRef, useState } from "react";
import { NodeResizer } from "reactflow";
import NodeDescription from "../GenericNode/components/NodeDescription";
import NoteToolbarComponent from "./NoteToolbarComponent";
function NoteNode({
data,
selected,
}: {
data: noteDataType;
selected: boolean;
data: NoteDataType;
selected?: boolean;
}) {
const bgColor =
Object.keys(COLOR_OPTIONS).find(

View file

@ -1,10 +1,10 @@
import { APIClassType } from "@/types/api";
import { EdgeType } from "@/types/flow";
import { cloneDeep } from "lodash";
import { Edge } from "reactflow";
export function processNodeAdvancedFields(
resData: APIClassType,
edges: Edge[],
edges: EdgeType[],
nodeId: string,
) {
let newNode = cloneDeep(resData);

View file

@ -4,10 +4,10 @@ import useAlertStore from "@/stores/alertStore";
import useFlowStore from "@/stores/flowStore";
import useFlowsManagerStore from "@/stores/flowsManagerStore";
import { APIClassType, InputFieldType } from "@/types/api";
import { NodeType } from "@/types/flow";
import { AllNodeType } from "@/types/flow";
import { useUpdateNodeInternals } from "@xyflow/react";
import { cloneDeep } from "lodash";
import { useCallback, useMemo } from "react";
import { useUpdateNodeInternals } from "reactflow";
import { mutateTemplate } from "../helpers/mutate-template";
export type handleOnNewValueType = (
@ -29,7 +29,7 @@ const useHandleOnNewValue = ({
name: string;
setNode?: (
id: string,
update: NodeType | ((oldState: NodeType) => NodeType),
update: AllNodeType | ((oldState: AllNodeType) => AllNodeType),
) => void;
}) => {
const takeSnapshot = useFlowsManagerStore((state) => state.takeSnapshot);

View file

@ -1,13 +1,13 @@
import useFlowStore from "@/stores/flowStore";
import { NodeType } from "@/types/flow";
import { AllNodeType } from "@/types/flow";
import { useUpdateNodeInternals } from "@xyflow/react";
import { cloneDeep } from "lodash";
import { useUpdateNodeInternals } from "reactflow";
const useHandleNodeClass = (
nodeId: string,
setMyNode?: (
id: string,
update: NodeType | ((oldState: NodeType) => NodeType),
update: AllNodeType | ((oldState: AllNodeType) => AllNodeType),
) => void,
) => {
const setNode = setMyNode ?? useFlowStore((state) => state.setNode);

View file

@ -1,7 +1,7 @@
import { AllNodeType } from "@/types/flow";
import { cloneDeep } from "lodash";
import { useCallback } from "react";
import { APIClassType } from "../../types/api";
import { NodeType } from "../../types/flow";
export type UpdateNodesType = {
nodeId: string;
@ -12,7 +12,9 @@ export type UpdateNodesType = {
};
const useUpdateAllNodes = (
setNodes: (callback: (oldNodes: NodeType[]) => NodeType[]) => void,
setNodes: (
update: AllNodeType[] | ((oldState: AllNodeType[]) => AllNodeType[]),
) => void,
updateNodeInternals: (nodeId: string) => void,
) => {
const updateAllNodes = useCallback(

View file

@ -4,8 +4,6 @@ import useSaveFlow from "@/hooks/flows/use-save-flow";
import useFlowsManagerStore from "@/stores/flowsManagerStore";
import useFlowStore from "@/stores/flowStore";
import { cn } from "@/utils/utils";
import { cloneDeep } from "lodash";
import { useEffect } from "react";
import {
ControlButton,
Panel,
@ -13,7 +11,9 @@ import {
useStore,
useStoreApi,
type ReactFlowState,
} from "reactflow";
} from "@xyflow/react";
import { cloneDeep } from "lodash";
import { useEffect } from "react";
import { shallow } from "zustand/shallow";
type CustomControlButtonProps = {
@ -106,7 +106,7 @@ const CanvasControls = ({ children }) => {
return (
<Panel
data-testid="canvas_controls"
className="react-flow__controls !m-2 flex gap-1.5 rounded-md border border-secondary-hover bg-background fill-foreground stroke-foreground p-1.5 text-primary shadow [&>button]:border-0 [&>button]:bg-background hover:[&>button]:bg-accent"
className="react-flow__controls !m-2 flex !flex-row gap-1.5 rounded-md border border-secondary-hover bg-background fill-foreground stroke-foreground p-1.5 text-primary shadow [&>button]:border-0 [&>button]:bg-background hover:[&>button]:bg-accent"
position="bottom-left"
>
{/* Zoom In */}

View file

@ -2,7 +2,7 @@ import AccordionComponent from "@/components/common/accordionComponent";
import ShadTooltip from "@/components/common/shadTooltipComponent";
import { EditNodeComponent } from "@/modals/editNodeModal/components/editNodeComponent";
import { APIClassType } from "@/types/api";
import { NodeType } from "@/types/flow";
import { AllNodeType } from "@/types/flow";
import { customStringify } from "@/utils/reactflowUtils";
import { useEffect, useState } from "react";
@ -11,7 +11,7 @@ export function TweakComponent({
node,
}: {
open: boolean;
node: NodeType;
node: AllNodeType;
}) {
const [nodeClass, setNodeClass] = useState<APIClassType | undefined>(
node.data?.node,

View file

@ -1,12 +1,12 @@
import { useTweaksStore } from "@/stores/tweaksStore";
import { NodeType } from "@/types/flow";
import { AllNodeType } from "@/types/flow";
import { TweakComponent } from "../tweakComponent";
export function TweaksComponent({ open }: { open: boolean }) {
const nodes = useTweaksStore((state) => state.nodes);
return (
<div className="h-full w-full overflow-y-auto overflow-x-hidden rounded-lg bg-muted custom-scroll">
{nodes?.map((node: NodeType, i) => (
{nodes?.map((node: AllNodeType, i) => (
<div className="px-3" key={i}>
<TweakComponent open={open} node={node} />
</div>

Some files were not shown because too many files have changed in this diff Show more