refactor: handle rendering and filtering functionality (#3512)
This commit is contained in:
parent
2bd5d4b911
commit
2f1f1808b9
25 changed files with 755 additions and 316 deletions
|
|
@ -145,6 +145,10 @@ body {
|
||||||
transition-duration: 150ms;
|
transition-duration: 150ms;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.react-flow__edge.running .react-flow__edge-path {
|
||||||
|
stroke: var(--status-blue) !important;
|
||||||
|
}
|
||||||
|
|
||||||
.ag-react-container {
|
.ag-react-container {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
|
|
||||||
33
src/frontend/src/CustomEdges/index.tsx
Normal file
33
src/frontend/src/CustomEdges/index.tsx
Normal file
|
|
@ -0,0 +1,33 @@
|
||||||
|
import useFlowStore from "@/stores/flowStore";
|
||||||
|
import { BaseEdge, EdgeProps, getBezierPath, Position } from "reactflow";
|
||||||
|
|
||||||
|
export function DefaultEdge({
|
||||||
|
sourceHandleId,
|
||||||
|
source,
|
||||||
|
sourceX,
|
||||||
|
sourceY,
|
||||||
|
target,
|
||||||
|
targetHandleId,
|
||||||
|
targetX,
|
||||||
|
targetY,
|
||||||
|
...props
|
||||||
|
}: EdgeProps) {
|
||||||
|
const getNode = useFlowStore((state) => state.getNode);
|
||||||
|
|
||||||
|
const sourceNode = getNode(source);
|
||||||
|
const targetNode = getNode(target);
|
||||||
|
|
||||||
|
const sourceXNew = (sourceNode?.position.x ?? 0) + (sourceNode?.width ?? 0);
|
||||||
|
const targetXNew = targetNode?.position.x ?? 0;
|
||||||
|
|
||||||
|
const [edgePath] = getBezierPath({
|
||||||
|
sourceX: sourceXNew,
|
||||||
|
sourceY,
|
||||||
|
sourcePosition: Position.Right,
|
||||||
|
targetPosition: Position.Left,
|
||||||
|
targetX: targetXNew,
|
||||||
|
targetY,
|
||||||
|
});
|
||||||
|
|
||||||
|
return <BaseEdge path={edgePath} {...props} />;
|
||||||
|
}
|
||||||
|
|
@ -1,30 +1,65 @@
|
||||||
import { TOOLTIP_EMPTY } from "../../../../constants/constants";
|
import { convertTestName } from "@/components/storeCardComponent/utils/convert-test-name";
|
||||||
import useFlowStore from "../../../../stores/flowStore";
|
|
||||||
import { useTypesStore } from "../../../../stores/typesStore";
|
|
||||||
import { NodeType } from "../../../../types/flow";
|
|
||||||
import { groupByFamily } from "../../../../utils/utils";
|
|
||||||
import TooltipRenderComponent from "../tooltipRenderComponent";
|
|
||||||
|
|
||||||
export default function HandleTooltips({
|
export default function HandleTooltipComponent({
|
||||||
left,
|
isInput,
|
||||||
tooltipTitle,
|
tooltipTitle,
|
||||||
|
colors,
|
||||||
|
isConnecting,
|
||||||
|
isCompatible,
|
||||||
|
isSameNode,
|
||||||
}: {
|
}: {
|
||||||
left: boolean;
|
isInput: boolean;
|
||||||
nodes: NodeType[];
|
colors: string[];
|
||||||
tooltipTitle: string;
|
tooltipTitle: string;
|
||||||
|
isConnecting: boolean;
|
||||||
|
isCompatible: boolean;
|
||||||
|
isSameNode: boolean;
|
||||||
}) {
|
}) {
|
||||||
const myData = useTypesStore((state) => state.data);
|
const tooltips = tooltipTitle.split("\n");
|
||||||
const nodes = useFlowStore((state) => state.nodes);
|
const plural = tooltips.length > 1 ? "s" : "";
|
||||||
|
return (
|
||||||
let groupedObj: any = groupByFamily(myData, tooltipTitle!, left, nodes!);
|
<div className="py-1.5 font-medium text-muted-foreground">
|
||||||
|
{isSameNode ? (
|
||||||
if (groupedObj && groupedObj.length > 0) {
|
"Can't connect to the same node"
|
||||||
//@ts-ignore
|
) : (
|
||||||
return groupedObj.map((item, index) => {
|
<div className="flex items-start gap-1.5">
|
||||||
return <TooltipRenderComponent index={index} item={item} left={left} />;
|
{isConnecting ? (
|
||||||
});
|
isCompatible ? (
|
||||||
} else {
|
<span>
|
||||||
//@ts-ignore
|
<span className="font-semibold text-foreground">Connect</span>{" "}
|
||||||
return <span data-testid={`empty-tooltip-filter`}>{TOOLTIP_EMPTY}</span>;
|
to
|
||||||
}
|
</span>
|
||||||
|
) : (
|
||||||
|
<span>Incompatible with</span>
|
||||||
|
)
|
||||||
|
) : (
|
||||||
|
<span className="text-foreground">
|
||||||
|
{isInput ? `Input${plural}` : `Output${plural}`}:{" "}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
{tooltips.map((word, index) => (
|
||||||
|
<div
|
||||||
|
className="rounded-sm px-1.5 text-background"
|
||||||
|
style={{ backgroundColor: colors[index] }}
|
||||||
|
data-testid={`${isInput ? "input" : "output"}-tooltip-${convertTestName(word)}`}
|
||||||
|
>
|
||||||
|
{word}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
{isConnecting && <span>{isInput ? `input` : `output`}</span>}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{!isConnecting && (
|
||||||
|
<div className="mt-2 flex flex-col gap-0.5 text-xs">
|
||||||
|
<div>
|
||||||
|
<b>Drag</b> to connect compatible {!isInput ? "inputs" : "outputs"}
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<b>Select</b> to filter compatible {!isInput ? "inputs" : "outputs"}{" "}
|
||||||
|
and components
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -81,6 +81,7 @@ export default function NodeInputField({
|
||||||
setFilterEdge={setFilterEdge}
|
setFilterEdge={setFilterEdge}
|
||||||
showNode={showNode}
|
showNode={showNode}
|
||||||
testIdComplement={`${data?.type?.toLowerCase()}-${showNode ? "shownode" : "noshownode"}`}
|
testIdComplement={`${data?.type?.toLowerCase()}-${showNode ? "shownode" : "noshownode"}`}
|
||||||
|
nodeId={data.id}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -110,6 +110,7 @@ export default function NodeOutputField({
|
||||||
id={id}
|
id={id}
|
||||||
title={title}
|
title={title}
|
||||||
edges={edges}
|
edges={edges}
|
||||||
|
nodeId={data.id}
|
||||||
myData={myData}
|
myData={myData}
|
||||||
colors={colors}
|
colors={colors}
|
||||||
setFilterEdge={setFilterEdge}
|
setFilterEdge={setFilterEdge}
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,6 @@
|
||||||
|
import { useDarkStore } from "@/stores/darkStore";
|
||||||
|
import useFlowStore from "@/stores/flowStore";
|
||||||
|
import { useMemo, useState } from "react";
|
||||||
import { Handle, Position } from "reactflow";
|
import { Handle, Position } from "reactflow";
|
||||||
import ShadTooltip from "../../../../components/shadTooltipComponent";
|
import ShadTooltip from "../../../../components/shadTooltipComponent";
|
||||||
import {
|
import {
|
||||||
|
|
@ -5,7 +8,7 @@ import {
|
||||||
scapedJSONStringfy,
|
scapedJSONStringfy,
|
||||||
} from "../../../../utils/reactflowUtils";
|
} from "../../../../utils/reactflowUtils";
|
||||||
import { classNames, cn, groupByFamily } from "../../../../utils/utils";
|
import { classNames, cn, groupByFamily } from "../../../../utils/utils";
|
||||||
import HandleTooltips from "../HandleTooltipComponent";
|
import HandleTooltipComponent from "../HandleTooltipComponent";
|
||||||
|
|
||||||
export default function HandleRenderComponent({
|
export default function HandleRenderComponent({
|
||||||
left,
|
left,
|
||||||
|
|
@ -20,6 +23,7 @@ export default function HandleRenderComponent({
|
||||||
setFilterEdge,
|
setFilterEdge,
|
||||||
showNode,
|
showNode,
|
||||||
testIdComplement,
|
testIdComplement,
|
||||||
|
nodeId,
|
||||||
}: {
|
}: {
|
||||||
left: boolean;
|
left: boolean;
|
||||||
nodes: any;
|
nodes: any;
|
||||||
|
|
@ -33,17 +37,168 @@ export default function HandleRenderComponent({
|
||||||
setFilterEdge: any;
|
setFilterEdge: any;
|
||||||
showNode: any;
|
showNode: any;
|
||||||
testIdComplement?: string;
|
testIdComplement?: string;
|
||||||
|
nodeId: string;
|
||||||
}) {
|
}) {
|
||||||
|
const setHandleDragging = useFlowStore((state) => state.setHandleDragging);
|
||||||
|
const setFilterType = useFlowStore((state) => state.setFilterType);
|
||||||
|
const handleDragging = useFlowStore((state) => state.handleDragging);
|
||||||
|
const filterType = useFlowStore((state) => state.filterType);
|
||||||
|
const dark = useDarkStore((state) => state.dark);
|
||||||
|
|
||||||
|
const onConnect = useFlowStore((state) => state.onConnect);
|
||||||
|
|
||||||
|
const handleMouseUp = () => {
|
||||||
|
setHandleDragging(undefined);
|
||||||
|
document.removeEventListener("mouseup", handleMouseUp);
|
||||||
|
};
|
||||||
|
|
||||||
|
const myId = useMemo(
|
||||||
|
() => scapedJSONStringfy(proxy ? { ...id, proxy } : id),
|
||||||
|
[id, proxy],
|
||||||
|
);
|
||||||
|
|
||||||
|
const getConnection = useMemo(
|
||||||
|
() =>
|
||||||
|
(semiConnection: {
|
||||||
|
source: string | undefined;
|
||||||
|
sourceHandle: string | undefined;
|
||||||
|
target: string | undefined;
|
||||||
|
targetHandle: string | undefined;
|
||||||
|
}) => ({
|
||||||
|
source: semiConnection.source ?? nodeId,
|
||||||
|
sourceHandle: semiConnection.sourceHandle ?? myId,
|
||||||
|
target: semiConnection.target ?? nodeId,
|
||||||
|
targetHandle: semiConnection.targetHandle ?? myId,
|
||||||
|
}),
|
||||||
|
[nodeId, myId],
|
||||||
|
);
|
||||||
|
|
||||||
|
const sameDraggingNode = useMemo(
|
||||||
|
() => (!left ? handleDragging?.target : handleDragging?.source) === nodeId,
|
||||||
|
[left, handleDragging, nodeId],
|
||||||
|
);
|
||||||
|
|
||||||
|
const ownDraggingHandle = useMemo(
|
||||||
|
() =>
|
||||||
|
handleDragging &&
|
||||||
|
(left ? handleDragging?.target : handleDragging?.source) &&
|
||||||
|
(left ? handleDragging.targetHandle : handleDragging.sourceHandle) ===
|
||||||
|
myId,
|
||||||
|
[handleDragging, left, myId],
|
||||||
|
);
|
||||||
|
|
||||||
|
const sameFilterNode = useMemo(
|
||||||
|
() => (!left ? filterType?.target : filterType?.source) === nodeId,
|
||||||
|
[left, filterType, nodeId],
|
||||||
|
);
|
||||||
|
|
||||||
|
const ownFilterHandle = useMemo(
|
||||||
|
() =>
|
||||||
|
filterType &&
|
||||||
|
(left ? filterType?.target : filterType?.source) === nodeId &&
|
||||||
|
(left ? filterType.targetHandle : filterType.sourceHandle) === myId,
|
||||||
|
[filterType, left, myId],
|
||||||
|
);
|
||||||
|
|
||||||
|
const sameNode = useMemo(
|
||||||
|
() => sameDraggingNode || sameFilterNode,
|
||||||
|
[sameDraggingNode, sameFilterNode],
|
||||||
|
);
|
||||||
|
const ownHandle = useMemo(
|
||||||
|
() => ownDraggingHandle || ownFilterHandle,
|
||||||
|
[ownDraggingHandle, ownFilterHandle],
|
||||||
|
);
|
||||||
|
|
||||||
|
const draggingOpenHandle = useMemo(
|
||||||
|
() =>
|
||||||
|
handleDragging &&
|
||||||
|
(left ? handleDragging.source : handleDragging.target) &&
|
||||||
|
!ownDraggingHandle
|
||||||
|
? isValidConnection(getConnection(handleDragging), nodes, edges)
|
||||||
|
: false,
|
||||||
|
[handleDragging, left, ownDraggingHandle, getConnection, nodes, edges],
|
||||||
|
);
|
||||||
|
|
||||||
|
const filterOpenHandle = useMemo(
|
||||||
|
() =>
|
||||||
|
filterType &&
|
||||||
|
(left ? filterType.source : filterType.target) &&
|
||||||
|
!ownFilterHandle
|
||||||
|
? isValidConnection(getConnection(filterType), nodes, edges)
|
||||||
|
: false,
|
||||||
|
[filterType, left, ownFilterHandle, getConnection, nodes, edges],
|
||||||
|
);
|
||||||
|
|
||||||
|
const openHandle = useMemo(
|
||||||
|
() => filterOpenHandle || draggingOpenHandle,
|
||||||
|
[filterOpenHandle, draggingOpenHandle],
|
||||||
|
);
|
||||||
|
const filterPresent = useMemo(
|
||||||
|
() => handleDragging || filterType,
|
||||||
|
[handleDragging, filterType],
|
||||||
|
);
|
||||||
|
|
||||||
|
const currentFilter = useMemo(
|
||||||
|
() =>
|
||||||
|
left
|
||||||
|
? {
|
||||||
|
targetHandle: myId,
|
||||||
|
target: nodeId,
|
||||||
|
source: undefined,
|
||||||
|
sourceHandle: undefined,
|
||||||
|
type: tooltipTitle,
|
||||||
|
color: colors[0],
|
||||||
|
}
|
||||||
|
: {
|
||||||
|
sourceHandle: myId,
|
||||||
|
source: nodeId,
|
||||||
|
target: undefined,
|
||||||
|
targetHandle: undefined,
|
||||||
|
type: tooltipTitle,
|
||||||
|
color: colors[0],
|
||||||
|
},
|
||||||
|
[left, myId, nodeId, tooltipTitle, colors],
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleColor = useMemo(
|
||||||
|
() =>
|
||||||
|
filterPresent && !(openHandle || ownHandle)
|
||||||
|
? dark
|
||||||
|
? "conic-gradient(#374151 0deg 360deg)"
|
||||||
|
: "conic-gradient(#cbd5e1 0deg 360deg)"
|
||||||
|
: "conic-gradient(" +
|
||||||
|
colors
|
||||||
|
.concat(colors[0])
|
||||||
|
.map(
|
||||||
|
(color, index) =>
|
||||||
|
color +
|
||||||
|
" " +
|
||||||
|
((360 / colors.length) * index - 360 / (colors.length * 4)) +
|
||||||
|
"deg " +
|
||||||
|
((360 / colors.length) * index + 360 / (colors.length * 4)) +
|
||||||
|
"deg",
|
||||||
|
)
|
||||||
|
.join(" ,") +
|
||||||
|
")",
|
||||||
|
[filterPresent, openHandle, ownHandle, dark, colors],
|
||||||
|
);
|
||||||
|
|
||||||
|
const [openTooltip, setOpenTooltip] = useState(false);
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<ShadTooltip
|
<ShadTooltip
|
||||||
|
open={openTooltip}
|
||||||
|
setOpen={setOpenTooltip}
|
||||||
styleClasses={"tooltip-fixed-width custom-scroll nowheel"}
|
styleClasses={"tooltip-fixed-width custom-scroll nowheel"}
|
||||||
delayDuration={1000}
|
delayDuration={1000}
|
||||||
content={
|
content={
|
||||||
<HandleTooltips
|
<HandleTooltipComponent
|
||||||
left={left}
|
isInput={left}
|
||||||
nodes={nodes}
|
colors={colors}
|
||||||
tooltipTitle={tooltipTitle!}
|
tooltipTitle={tooltipTitle}
|
||||||
|
isConnecting={!!filterPresent && !ownHandle}
|
||||||
|
isCompatible={openHandle}
|
||||||
|
isSameNode={sameNode && !ownHandle}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
side={left ? "left" : "right"}
|
side={left ? "left" : "right"}
|
||||||
|
|
@ -54,47 +209,73 @@ export default function HandleRenderComponent({
|
||||||
}`}
|
}`}
|
||||||
type={left ? "target" : "source"}
|
type={left ? "target" : "source"}
|
||||||
position={left ? Position.Left : Position.Right}
|
position={left ? Position.Left : Position.Right}
|
||||||
key={scapedJSONStringfy(proxy ? { ...id, proxy } : id)}
|
key={myId}
|
||||||
id={scapedJSONStringfy(proxy ? { ...id, proxy } : id)}
|
id={myId}
|
||||||
isValidConnection={(connection) =>
|
isValidConnection={(connection) =>
|
||||||
isValidConnection(connection, nodes, edges)
|
isValidConnection(connection, nodes, edges)
|
||||||
}
|
}
|
||||||
className={classNames(
|
className={classNames(
|
||||||
left ? "-ml-0.5" : "-mr-0.5",
|
`group/handle z-20 h-6 w-6 rounded-full border-none bg-transparent transition-all`,
|
||||||
"z-20 h-3 w-3 rounded-full border-none bg-background",
|
|
||||||
)}
|
)}
|
||||||
style={{
|
|
||||||
background:
|
|
||||||
"conic-gradient(" +
|
|
||||||
colors
|
|
||||||
.concat(colors[0])
|
|
||||||
.map(
|
|
||||||
(color, index) =>
|
|
||||||
color +
|
|
||||||
" " +
|
|
||||||
((360 / colors.length) * index -
|
|
||||||
360 / (colors.length * 4)) +
|
|
||||||
"deg " +
|
|
||||||
((360 / colors.length) * index +
|
|
||||||
360 / (colors.length * 4)) +
|
|
||||||
"deg",
|
|
||||||
)
|
|
||||||
.join(" ,") +
|
|
||||||
")",
|
|
||||||
WebkitMaskImage: "radial-gradient(transparent 40%, black 44%)",
|
|
||||||
maskImage: "radial-gradient(transparent 40%, black 44%)",
|
|
||||||
}}
|
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setFilterEdge(groupByFamily(myData, tooltipTitle!, left, nodes!));
|
setFilterEdge(groupByFamily(myData, tooltipTitle!, left, nodes!));
|
||||||
|
setFilterType(currentFilter);
|
||||||
|
if (filterOpenHandle && filterType) {
|
||||||
|
onConnect(getConnection(filterType));
|
||||||
|
setFilterType(undefined);
|
||||||
|
setFilterEdge([]);
|
||||||
|
}
|
||||||
}}
|
}}
|
||||||
/>
|
onMouseUp={() => {
|
||||||
</ShadTooltip>
|
setOpenTooltip(false);
|
||||||
|
}}
|
||||||
|
onContextMenu={(event) => {
|
||||||
|
event.preventDefault();
|
||||||
|
}}
|
||||||
|
onMouseDown={(event) => {
|
||||||
|
if (event.button === 0) {
|
||||||
|
setHandleDragging(currentFilter);
|
||||||
|
document.addEventListener("mouseup", handleMouseUp);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
"absolute top-[50%] z-10 h-3 w-3 translate-y-[-50%] rounded-full bg-background",
|
"pointer-events-none absolute left-1/2 top-[50%] z-30 flex h-0 w-0 -translate-x-1/2 translate-y-[-50%] items-center justify-center rounded-full bg-background transition-all group-hover/handle:bg-transparent",
|
||||||
left ? "-left-[4px] -ml-0.5" : "-right-[4px] -mr-0.5",
|
filterPresent
|
||||||
|
? openHandle || ownHandle
|
||||||
|
? cn(
|
||||||
|
"h-4 w-4",
|
||||||
|
ownHandle ? "bg-transparent" : "bg-background",
|
||||||
|
)
|
||||||
|
: ""
|
||||||
|
: "group-hover/node:h-4 group-hover/node:w-4",
|
||||||
)}
|
)}
|
||||||
|
></div>
|
||||||
|
<div
|
||||||
|
className="pointer-events-none absolute left-1/2 top-[50%] z-10 flex h-3 w-3 -translate-x-1/2 translate-y-[-50%] items-center justify-center rounded-full opacity-50 transition-all"
|
||||||
|
style={{
|
||||||
|
background: handleColor,
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
|
<div
|
||||||
|
data-testid={`gradient-handle-${testIdComplement}-${title.toLowerCase()}-${
|
||||||
|
!showNode ? (left ? "target" : "source") : left ? "left" : "right"
|
||||||
|
}`}
|
||||||
|
className={classNames(
|
||||||
|
`pointer-events-none absolute left-1/2 top-[50%] z-10 flex -translate-x-1/2 translate-y-[-50%] items-center justify-center rounded-full transition-all`,
|
||||||
|
filterPresent
|
||||||
|
? openHandle || ownHandle
|
||||||
|
? cn("h-5 w-5")
|
||||||
|
: cn("h-1.5 w-1.5")
|
||||||
|
: cn("h-1.5 w-1.5 group-hover/node:h-5 group-hover/node:w-5"),
|
||||||
|
)}
|
||||||
|
style={{
|
||||||
|
background: handleColor,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Handle>
|
||||||
|
</ShadTooltip>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,91 +0,0 @@
|
||||||
import React from "react";
|
|
||||||
import {
|
|
||||||
INPUT_HANDLER_HOVER,
|
|
||||||
OUTPUT_HANDLER_HOVER,
|
|
||||||
} from "../../../../constants/constants";
|
|
||||||
import {
|
|
||||||
nodeColors,
|
|
||||||
nodeIconsLucide,
|
|
||||||
nodeNames,
|
|
||||||
} from "../../../../utils/styleUtils";
|
|
||||||
import { classNames } from "../../../../utils/utils";
|
|
||||||
|
|
||||||
const TooltipRenderComponent = ({ item, index, left }) => {
|
|
||||||
const Icon = nodeIconsLucide[item.family] ?? nodeIconsLucide["unknown"];
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
key={index}
|
|
||||||
data-testid={`available-${left ? "input" : "output"}-${item.family}`}
|
|
||||||
>
|
|
||||||
{index === 0 && (
|
|
||||||
<span>{left ? INPUT_HANDLER_HOVER : OUTPUT_HANDLER_HOVER}</span>
|
|
||||||
)}
|
|
||||||
<span
|
|
||||||
key={index}
|
|
||||||
className={classNames(
|
|
||||||
index > 0 ? "mt-2 flex items-center" : "mt-3 flex items-center",
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
className="h-5 w-5"
|
|
||||||
style={{
|
|
||||||
color: nodeColors[item.family],
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Icon
|
|
||||||
className="h-5 w-5"
|
|
||||||
strokeWidth={1.5}
|
|
||||||
style={{
|
|
||||||
color: nodeColors[item.family] ?? nodeColors.unknown,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<span
|
|
||||||
className="ps-2 text-xs text-foreground"
|
|
||||||
data-testid={`tooltip-${nodeNames[item.family] ?? "Other"}`}
|
|
||||||
>
|
|
||||||
{nodeNames[item.family] ?? "Other"}{" "}
|
|
||||||
{item?.display_name && item?.display_name?.length > 0 ? (
|
|
||||||
<span
|
|
||||||
className="text-xs"
|
|
||||||
data-testid={`tooltip-${item?.display_name}`}
|
|
||||||
>
|
|
||||||
{" "}
|
|
||||||
{item.display_name === "" ? "" : " - "}
|
|
||||||
{item.display_name.split(", ").length > 2
|
|
||||||
? item.display_name.split(", ").map((el, index) => (
|
|
||||||
<React.Fragment key={el + name}>
|
|
||||||
<span>
|
|
||||||
{index === item.display_name.split(", ").length - 1
|
|
||||||
? el
|
|
||||||
: (el += `, `)}
|
|
||||||
</span>
|
|
||||||
</React.Fragment>
|
|
||||||
))
|
|
||||||
: item.display_name}
|
|
||||||
</span>
|
|
||||||
) : (
|
|
||||||
<span className="text-xs" data-testid={`tooltip-${item?.type}`}>
|
|
||||||
{" "}
|
|
||||||
{item.type === "" ? "" : " - "}
|
|
||||||
{item.type.split(", ").length > 2
|
|
||||||
? item.type.split(", ").map((el, index) => (
|
|
||||||
<React.Fragment key={el + name}>
|
|
||||||
<span>
|
|
||||||
{index === item.type.split(", ").length - 1
|
|
||||||
? el
|
|
||||||
: (el += `, `)}
|
|
||||||
</span>
|
|
||||||
</React.Fragment>
|
|
||||||
))
|
|
||||||
: item.type}
|
|
||||||
</span>
|
|
||||||
)}
|
|
||||||
</span>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default TooltipRenderComponent;
|
|
||||||
|
|
@ -9,12 +9,19 @@ export default function ShadTooltip({
|
||||||
children,
|
children,
|
||||||
styleClasses,
|
styleClasses,
|
||||||
delayDuration = 500,
|
delayDuration = 500,
|
||||||
|
open,
|
||||||
|
setOpen,
|
||||||
}: ShadToolTipType): JSX.Element {
|
}: ShadToolTipType): JSX.Element {
|
||||||
return content ? (
|
return content ? (
|
||||||
<Tooltip defaultOpen={!children} delayDuration={delayDuration}>
|
<Tooltip
|
||||||
|
defaultOpen={!children}
|
||||||
|
open={open}
|
||||||
|
onOpenChange={setOpen}
|
||||||
|
delayDuration={delayDuration}
|
||||||
|
>
|
||||||
<TooltipTrigger asChild={asChild}>{children}</TooltipTrigger>
|
<TooltipTrigger asChild={asChild}>{children}</TooltipTrigger>
|
||||||
<TooltipContent
|
<TooltipContent
|
||||||
className={cn(styleClasses, "max-w-96")}
|
className={cn("max-w-96", styleClasses)}
|
||||||
side={side}
|
side={side}
|
||||||
avoidCollisions={false}
|
avoidCollisions={false}
|
||||||
sticky="always"
|
sticky="always"
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { DefaultEdge } from "@/CustomEdges";
|
||||||
import NoteNode from "@/CustomNodes/NoteNode";
|
import NoteNode from "@/CustomNodes/NoteNode";
|
||||||
import IconComponent from "@/components/genericIconComponent";
|
import IconComponent from "@/components/genericIconComponent";
|
||||||
import LoadingComponent from "@/components/loadingComponent";
|
import LoadingComponent from "@/components/loadingComponent";
|
||||||
|
|
@ -8,7 +9,6 @@ import useAutoSaveFlow from "@/hooks/flows/use-autosave-flow";
|
||||||
import useUploadFlow from "@/hooks/flows/use-upload-flow";
|
import useUploadFlow from "@/hooks/flows/use-upload-flow";
|
||||||
import { getNodeRenderType, isSupportedNodeTypes } from "@/utils/utils";
|
import { getNodeRenderType, isSupportedNodeTypes } from "@/utils/utils";
|
||||||
|
|
||||||
import { ENABLE_MVPS } from "@/customization/feature-flags";
|
|
||||||
import _, { cloneDeep } from "lodash";
|
import _, { cloneDeep } from "lodash";
|
||||||
import {
|
import {
|
||||||
KeyboardEvent,
|
KeyboardEvent,
|
||||||
|
|
@ -457,6 +457,8 @@ export default function Page({ view }: { view?: boolean }): JSX.Element {
|
||||||
onSelectionDragStart={onSelectionDragStart}
|
onSelectionDragStart={onSelectionDragStart}
|
||||||
onSelectionEnd={onSelectionEnd}
|
onSelectionEnd={onSelectionEnd}
|
||||||
onSelectionStart={onSelectionStart}
|
onSelectionStart={onSelectionStart}
|
||||||
|
connectionRadius={25}
|
||||||
|
edgeTypes={{ default: DefaultEdge }}
|
||||||
connectionLineComponent={ConnectionLineComponent}
|
connectionLineComponent={ConnectionLineComponent}
|
||||||
onDragOver={onDragOver}
|
onDragOver={onDragOver}
|
||||||
onNodeDragStop={onNodeDragStop}
|
onNodeDragStop={onNodeDragStop}
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ export default function ParentDisclosureComponent({
|
||||||
data-testid={testId}
|
data-testid={testId}
|
||||||
>
|
>
|
||||||
<div className="flex items-baseline gap-1 align-baseline">
|
<div className="flex items-baseline gap-1 align-baseline">
|
||||||
<span className="parent-disclosure-title">{title}</span>
|
<span className="text-sm font-medium">{title}</span>
|
||||||
{beta && (
|
{beta && (
|
||||||
<div className="h-fit rounded-full bg-beta-background px-2 py-1 text-xs/3 font-semibold text-beta-foreground-soft">
|
<div className="h-fit rounded-full bg-beta-background px-2 py-1 text-xs/3 font-semibold text-beta-foreground-soft">
|
||||||
BETA
|
BETA
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@ import { nodeIconsLucide } from "../../../../utils/styleUtils";
|
||||||
import ParentDisclosureComponent from "../ParentDisclosureComponent";
|
import ParentDisclosureComponent from "../ParentDisclosureComponent";
|
||||||
import { SidebarCategoryComponent } from "./SidebarCategoryComponent";
|
import { SidebarCategoryComponent } from "./SidebarCategoryComponent";
|
||||||
|
|
||||||
|
import { SidebarFilterComponent } from "./sidebarFilterComponent";
|
||||||
import { sortKeys } from "./utils";
|
import { sortKeys } from "./utils";
|
||||||
|
|
||||||
export default function ExtraSidebar(): JSX.Element {
|
export default function ExtraSidebar(): JSX.Element {
|
||||||
|
|
@ -25,6 +26,7 @@ export default function ExtraSidebar(): JSX.Element {
|
||||||
const getFilterEdge = useFlowStore((state) => state.getFilterEdge);
|
const getFilterEdge = useFlowStore((state) => state.getFilterEdge);
|
||||||
const setFilterEdge = useFlowStore((state) => state.setFilterEdge);
|
const setFilterEdge = useFlowStore((state) => state.setFilterEdge);
|
||||||
const hasStore = useStoreStore((state) => state.hasStore);
|
const hasStore = useStoreStore((state) => state.hasStore);
|
||||||
|
const filterType = useFlowStore((state) => state.filterType);
|
||||||
|
|
||||||
const setErrorData = useAlertStore((state) => state.setErrorData);
|
const setErrorData = useAlertStore((state) => state.setErrorData);
|
||||||
const [dataFilter, setFilterData] = useState(data);
|
const [dataFilter, setFilterData] = useState(data);
|
||||||
|
|
@ -222,8 +224,18 @@ export default function ExtraSidebar(): JSX.Element {
|
||||||
|
|
||||||
<div className="side-bar-components-div-arrangement">
|
<div className="side-bar-components-div-arrangement">
|
||||||
<div className="parent-disclosure-arrangement">
|
<div className="parent-disclosure-arrangement">
|
||||||
<div className="flex items-center gap-4 align-middle">
|
<div className="flex w-full flex-col items-start justify-between gap-2.5">
|
||||||
<span className="parent-disclosure-title">Components</span>
|
<span className="text-sm font-medium">Components</span>
|
||||||
|
{filterType && (
|
||||||
|
<SidebarFilterComponent
|
||||||
|
isInput={!!filterType.source}
|
||||||
|
type={filterType.type}
|
||||||
|
resetFilters={() => {
|
||||||
|
setFilterEdge([]);
|
||||||
|
setFilterData(data);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<Separator />
|
<Separator />
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,40 @@
|
||||||
|
import ForwardedIconComponent from "@/components/genericIconComponent";
|
||||||
|
import ShadTooltip from "@/components/shadTooltipComponent";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
|
||||||
|
export function SidebarFilterComponent({
|
||||||
|
isInput,
|
||||||
|
type,
|
||||||
|
resetFilters,
|
||||||
|
}: {
|
||||||
|
isInput: boolean;
|
||||||
|
type: string;
|
||||||
|
resetFilters: () => void;
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<div className="mb-0.5 flex w-full items-center justify-between rounded border bg-muted p-1 px-2 text-xs font-medium text-primary">
|
||||||
|
<div className="flex flex-1 items-center gap-1.5">
|
||||||
|
<ForwardedIconComponent
|
||||||
|
name="ListFilter"
|
||||||
|
className="h-4 w-4 shrink-0 stroke-2"
|
||||||
|
/>
|
||||||
|
<div className="flex-1 overflow-hidden truncate">
|
||||||
|
{isInput ? "Input" : "Output"}: {type}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<ShadTooltip
|
||||||
|
side="right"
|
||||||
|
styleClasses="max-w-full"
|
||||||
|
content="Remove filter"
|
||||||
|
>
|
||||||
|
<Button unstyled className="shrink-0" onClick={resetFilters}>
|
||||||
|
<ForwardedIconComponent
|
||||||
|
name="X"
|
||||||
|
className="h-4 w-4 stroke-2"
|
||||||
|
aria-hidden="true"
|
||||||
|
/>
|
||||||
|
</Button>
|
||||||
|
</ShadTooltip>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -431,6 +431,9 @@ const useFlowStore = create<FlowStoreType>((set, get) => ({
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
setFilterEdge: (newState) => {
|
setFilterEdge: (newState) => {
|
||||||
|
if (newState.length === 0) {
|
||||||
|
set({ filterType: undefined });
|
||||||
|
}
|
||||||
set({ getFilterEdge: newState });
|
set({ getFilterEdge: newState });
|
||||||
},
|
},
|
||||||
getFilterEdge: [],
|
getFilterEdge: [],
|
||||||
|
|
@ -469,8 +472,6 @@ const useFlowStore = create<FlowStoreType>((set, get) => ({
|
||||||
targetHandle: scapeJSONParse(connection.targetHandle!),
|
targetHandle: scapeJSONParse(connection.targetHandle!),
|
||||||
sourceHandle: scapeJSONParse(connection.sourceHandle!),
|
sourceHandle: scapeJSONParse(connection.sourceHandle!),
|
||||||
},
|
},
|
||||||
// style: { stroke: "#555" },
|
|
||||||
// className: "stroke-foreground stroke-connection",
|
|
||||||
},
|
},
|
||||||
oldEdges,
|
oldEdges,
|
||||||
);
|
);
|
||||||
|
|
@ -528,6 +529,7 @@ const useFlowStore = create<FlowStoreType>((set, get) => ({
|
||||||
get().updateBuildStatus(ids, BuildStatus.ERROR);
|
get().updateBuildStatus(ids, BuildStatus.ERROR);
|
||||||
throw new Error("Invalid components");
|
throw new Error("Invalid components");
|
||||||
}
|
}
|
||||||
|
get().updateEdgesRunningByNodes(nodes, true);
|
||||||
}
|
}
|
||||||
function handleBuildUpdate(
|
function handleBuildUpdate(
|
||||||
vertexBuildData: VertexBuildTypeAPI,
|
vertexBuildData: VertexBuildTypeAPI,
|
||||||
|
|
@ -631,6 +633,10 @@ const useFlowStore = create<FlowStoreType>((set, get) => ({
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
get().updateEdgesRunningByNodes(
|
||||||
|
get().nodes.map((n) => n.id),
|
||||||
|
false,
|
||||||
|
);
|
||||||
get().setIsBuilding(false);
|
get().setIsBuilding(false);
|
||||||
get().setLockChat(false);
|
get().setLockChat(false);
|
||||||
},
|
},
|
||||||
|
|
@ -653,6 +659,10 @@ const useFlowStore = create<FlowStoreType>((set, get) => ({
|
||||||
title:
|
title:
|
||||||
"There are outdated components in the flow. The error could be related to them.",
|
"There are outdated components in the flow. The error could be related to them.",
|
||||||
});
|
});
|
||||||
|
get().updateEdgesRunningByNodes(
|
||||||
|
get().nodes.map((n) => n.id),
|
||||||
|
false,
|
||||||
|
);
|
||||||
setErrorData({ list, title });
|
setErrorData({ list, title });
|
||||||
get().setIsBuilding(false);
|
get().setIsBuilding(false);
|
||||||
get().setLockChat(false);
|
get().setLockChat(false);
|
||||||
|
|
@ -662,7 +672,7 @@ const useFlowStore = create<FlowStoreType>((set, get) => ({
|
||||||
// reference is the id of the vertex or the id of the parent in a group node
|
// reference is the id of the vertex or the id of the parent in a group node
|
||||||
.map((element) => element.reference)
|
.map((element) => element.reference)
|
||||||
.filter(Boolean) as string[];
|
.filter(Boolean) as string[];
|
||||||
useFlowStore.getState().updateBuildStatus(idList, BuildStatus.BUILDING);
|
get().updateBuildStatus(idList, BuildStatus.BUILDING);
|
||||||
},
|
},
|
||||||
onValidateNodes: validateSubgraph,
|
onValidateNodes: validateSubgraph,
|
||||||
nodes: get().nodes || undefined,
|
nodes: get().nodes || undefined,
|
||||||
|
|
@ -680,6 +690,17 @@ const useFlowStore = create<FlowStoreType>((set, get) => ({
|
||||||
viewport: get().reactFlowInstance?.getViewport()!,
|
viewport: get().reactFlowInstance?.getViewport()!,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
updateEdgesRunningByNodes: (ids: string[], running: boolean) => {
|
||||||
|
const edges = get().edges;
|
||||||
|
const newEdges = edges.map((edge) => {
|
||||||
|
if (ids.includes(edge.source) && ids.includes(edge.target)) {
|
||||||
|
edge.animated = running;
|
||||||
|
edge.className = running ? "running" : "";
|
||||||
|
}
|
||||||
|
return edge;
|
||||||
|
});
|
||||||
|
set({ edges: newEdges });
|
||||||
|
},
|
||||||
updateVerticesBuild: (
|
updateVerticesBuild: (
|
||||||
vertices: {
|
vertices: {
|
||||||
verticesIds: string[];
|
verticesIds: string[];
|
||||||
|
|
@ -762,6 +783,15 @@ const useFlowStore = create<FlowStoreType>((set, get) => ({
|
||||||
setBuildController: (controller) => {
|
setBuildController: (controller) => {
|
||||||
set({ buildController: controller });
|
set({ buildController: controller });
|
||||||
},
|
},
|
||||||
|
handleDragging: undefined,
|
||||||
|
setHandleDragging: (handleDragging) => {
|
||||||
|
set({ handleDragging });
|
||||||
|
},
|
||||||
|
|
||||||
|
filterType: undefined,
|
||||||
|
setFilterType: (filterType) => {
|
||||||
|
set({ filterType });
|
||||||
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
export default useFlowStore;
|
export default useFlowStore;
|
||||||
|
|
|
||||||
|
|
@ -231,7 +231,7 @@
|
||||||
@apply fill-chat-trigger-disabled stroke-chat-trigger-disabled stroke-1;
|
@apply fill-chat-trigger-disabled stroke-chat-trigger-disabled stroke-1;
|
||||||
}
|
}
|
||||||
.parent-disclosure-arrangement {
|
.parent-disclosure-arrangement {
|
||||||
@apply flex w-full select-none items-center justify-between bg-background px-3 py-1;
|
@apply flex w-full select-none items-center justify-between bg-background px-5 py-3;
|
||||||
}
|
}
|
||||||
.components-disclosure-arrangement {
|
.components-disclosure-arrangement {
|
||||||
@apply -mt-px flex w-full select-none items-center justify-between border-y border-y-input bg-muted px-3 py-2;
|
@apply -mt-px flex w-full select-none items-center justify-between border-y border-y-input bg-muted px-3 py-2;
|
||||||
|
|
@ -240,9 +240,6 @@
|
||||||
/* different color than the non child */
|
/* different color than the non child */
|
||||||
@apply -mt-px flex w-full select-none items-center justify-between border-y border-y-input bg-muted px-3 py-2;
|
@apply -mt-px flex w-full select-none items-center justify-between border-y border-y-input bg-muted px-3 py-2;
|
||||||
}
|
}
|
||||||
.parent-disclosure-title {
|
|
||||||
@apply p-2 px-2 text-sm font-medium;
|
|
||||||
}
|
|
||||||
.components-disclosure-title {
|
.components-disclosure-title {
|
||||||
@apply flex items-center text-sm text-primary;
|
@apply flex items-center text-sm text-primary;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -169,6 +169,26 @@ textarea[class^="ag-"]:focus {
|
||||||
cursor: grabbing !important;
|
cursor: grabbing !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.react-flow__handle-right {
|
||||||
|
right: 0 !important;
|
||||||
|
transform: translate(50%, -50%) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.react-flow__handle-left {
|
||||||
|
left: 0 !important;
|
||||||
|
transform: translate(-50%, -50%) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.react-flow__handle-right {
|
||||||
|
right: 0 !important;
|
||||||
|
transform: translate(50%, -50%) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.react-flow__handle-left {
|
||||||
|
left: 0 !important;
|
||||||
|
transform: translate(-50%, -50%) !important;
|
||||||
|
}
|
||||||
|
|
||||||
.react-flow__node-noteNode:not(.selected) {
|
.react-flow__node-noteNode:not(.selected) {
|
||||||
z-index: -1 !important;
|
z-index: -1 !important;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -57,6 +57,8 @@
|
||||||
--hover: #f2f4f5;
|
--hover: #f2f4f5;
|
||||||
--disabled-run: #6366f1;
|
--disabled-run: #6366f1;
|
||||||
|
|
||||||
|
--filter-foreground: #4f46e5;
|
||||||
|
--filter-background: #eef2ff;
|
||||||
/* Colors that are shared in dark and light mode */
|
/* Colors that are shared in dark and light mode */
|
||||||
--blur-shared: #151923de;
|
--blur-shared: #151923de;
|
||||||
--build-trigger: #dc735b;
|
--build-trigger: #dc735b;
|
||||||
|
|
@ -114,6 +116,9 @@
|
||||||
--destructive: 0 60% 25%; /* hsl(0 60% 25%) */
|
--destructive: 0 60% 25%; /* hsl(0 60% 25%) */
|
||||||
--destructive-foreground: 210 40% 98%; /* hsl(210 40% 98%) */
|
--destructive-foreground: 210 40% 98%; /* hsl(210 40% 98%) */
|
||||||
|
|
||||||
|
--filter-foreground: #eef2ff;
|
||||||
|
--filter-background: #4e46e599;
|
||||||
|
|
||||||
--ring: 216 24% 30%; /* hsl(216 24% 30%) */
|
--ring: 216 24% 30%; /* hsl(216 24% 30%) */
|
||||||
|
|
||||||
--radius: 0.5rem;
|
--radius: 0.5rem;
|
||||||
|
|
|
||||||
|
|
@ -339,6 +339,8 @@ export type ShadTooltipProps = {
|
||||||
style?: string;
|
style?: string;
|
||||||
};
|
};
|
||||||
export type ShadToolTipType = {
|
export type ShadToolTipType = {
|
||||||
|
open?: boolean;
|
||||||
|
setOpen?: (open: boolean) => void;
|
||||||
content?: ReactNode | null;
|
content?: ReactNode | null;
|
||||||
side?: "top" | "right" | "bottom" | "left";
|
side?: "top" | "right" | "bottom" | "left";
|
||||||
asChild?: boolean;
|
asChild?: boolean;
|
||||||
|
|
|
||||||
|
|
@ -180,6 +180,52 @@ export type FlowStoreType = {
|
||||||
edges?: Edge[];
|
edges?: Edge[];
|
||||||
viewport?: Viewport;
|
viewport?: Viewport;
|
||||||
}) => void;
|
}) => void;
|
||||||
|
handleDragging:
|
||||||
|
| {
|
||||||
|
source: string | undefined;
|
||||||
|
sourceHandle: string | undefined;
|
||||||
|
target: string | undefined;
|
||||||
|
targetHandle: string | undefined;
|
||||||
|
type: string;
|
||||||
|
color: string;
|
||||||
|
}
|
||||||
|
| undefined;
|
||||||
|
setHandleDragging: (
|
||||||
|
data:
|
||||||
|
| {
|
||||||
|
source: string | undefined;
|
||||||
|
sourceHandle: string | undefined;
|
||||||
|
target: string | undefined;
|
||||||
|
targetHandle: string | undefined;
|
||||||
|
type: string;
|
||||||
|
color: string;
|
||||||
|
}
|
||||||
|
| undefined,
|
||||||
|
) => void;
|
||||||
|
|
||||||
|
filterType:
|
||||||
|
| {
|
||||||
|
source: string | undefined;
|
||||||
|
sourceHandle: string | undefined;
|
||||||
|
target: string | undefined;
|
||||||
|
targetHandle: string | undefined;
|
||||||
|
type: string;
|
||||||
|
color: string;
|
||||||
|
}
|
||||||
|
| undefined;
|
||||||
|
setFilterType: (
|
||||||
|
data:
|
||||||
|
| {
|
||||||
|
source: string | undefined;
|
||||||
|
sourceHandle: string | undefined;
|
||||||
|
target: string | undefined;
|
||||||
|
targetHandle: string | undefined;
|
||||||
|
type: string;
|
||||||
|
color: string;
|
||||||
|
}
|
||||||
|
| undefined,
|
||||||
|
) => void;
|
||||||
|
updateEdgesRunningByNodes: (ids: string[], running: boolean) => void;
|
||||||
stopBuilding: () => void;
|
stopBuilding: () => void;
|
||||||
buildController: AbortController;
|
buildController: AbortController;
|
||||||
setBuildController: (controller: AbortController) => void;
|
setBuildController: (controller: AbortController) => void;
|
||||||
|
|
|
||||||
|
|
@ -215,6 +215,9 @@ export function isValidConnection(
|
||||||
nodes: Node[],
|
nodes: Node[],
|
||||||
edges: Edge[],
|
edges: Edge[],
|
||||||
) {
|
) {
|
||||||
|
if (source === target) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
const targetHandleObject: targetHandleType = scapeJSONParse(targetHandle!);
|
const targetHandleObject: targetHandleType = scapeJSONParse(targetHandle!);
|
||||||
const sourceHandleObject: sourceHandleType = scapeJSONParse(sourceHandle!);
|
const sourceHandleObject: sourceHandleType = scapeJSONParse(sourceHandle!);
|
||||||
if (
|
if (
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ import {
|
||||||
AlertTriangle,
|
AlertTriangle,
|
||||||
ArrowBigUp,
|
ArrowBigUp,
|
||||||
ArrowLeft,
|
ArrowLeft,
|
||||||
|
ArrowRight,
|
||||||
ArrowUpToLine,
|
ArrowUpToLine,
|
||||||
Bell,
|
Bell,
|
||||||
Binary,
|
Binary,
|
||||||
|
|
@ -63,6 +64,7 @@ import {
|
||||||
FileText,
|
FileText,
|
||||||
FileType2,
|
FileType2,
|
||||||
FileUp,
|
FileUp,
|
||||||
|
Filter,
|
||||||
FlaskConical,
|
FlaskConical,
|
||||||
FolderIcon,
|
FolderIcon,
|
||||||
FolderPlus,
|
FolderPlus,
|
||||||
|
|
@ -87,6 +89,7 @@ import {
|
||||||
Layers,
|
Layers,
|
||||||
Link,
|
Link,
|
||||||
Link2,
|
Link2,
|
||||||
|
ListFilter,
|
||||||
Loader2,
|
Loader2,
|
||||||
Lock,
|
Lock,
|
||||||
LogIn,
|
LogIn,
|
||||||
|
|
@ -401,10 +404,12 @@ export const nodeIconsLucide: iconsType = {
|
||||||
GoogleSearchRun: GoogleIcon,
|
GoogleSearchRun: GoogleIcon,
|
||||||
Google: GoogleIcon,
|
Google: GoogleIcon,
|
||||||
GoogleGenerativeAI: GoogleGenerativeAIIcon,
|
GoogleGenerativeAI: GoogleGenerativeAIIcon,
|
||||||
|
ArrowRight,
|
||||||
Groq: GroqIcon,
|
Groq: GroqIcon,
|
||||||
HCD: HCDIcon,
|
HCD: HCDIcon,
|
||||||
HNLoader: HackerNewsIcon,
|
HNLoader: HackerNewsIcon,
|
||||||
Unstructured: UnstructuredIcon,
|
Unstructured: UnstructuredIcon,
|
||||||
|
Filter: Filter,
|
||||||
HuggingFaceHub: HuggingFaceIcon,
|
HuggingFaceHub: HuggingFaceIcon,
|
||||||
HuggingFace: HuggingFaceIcon,
|
HuggingFace: HuggingFaceIcon,
|
||||||
HuggingFaceEmbeddings: HuggingFaceIcon,
|
HuggingFaceEmbeddings: HuggingFaceIcon,
|
||||||
|
|
@ -414,6 +419,7 @@ export const nodeIconsLucide: iconsType = {
|
||||||
Meta: MetaIcon,
|
Meta: MetaIcon,
|
||||||
CheckCheck,
|
CheckCheck,
|
||||||
Midjorney: MidjourneyIcon,
|
Midjorney: MidjourneyIcon,
|
||||||
|
ListFilter,
|
||||||
MongoDBAtlasVectorSearch: MongoDBIcon,
|
MongoDBAtlasVectorSearch: MongoDBIcon,
|
||||||
MongoDB: MongoDBIcon,
|
MongoDB: MongoDBIcon,
|
||||||
MongoDBChatMessageHistory: MongoDBIcon,
|
MongoDBChatMessageHistory: MongoDBIcon,
|
||||||
|
|
|
||||||
|
|
@ -97,9 +97,15 @@ const config = {
|
||||||
"status-gray": "var(--status-gray)",
|
"status-gray": "var(--status-gray)",
|
||||||
"success-background": "var(--success-background)",
|
"success-background": "var(--success-background)",
|
||||||
"success-foreground": "var(--success-foreground)",
|
"success-foreground": "var(--success-foreground)",
|
||||||
"beta-background": "var(--beta-background)",
|
filter: {
|
||||||
"beta-foreground": "var(--beta-foreground)",
|
foreground: "var(--filter-foreground)",
|
||||||
"beta-foreground-soft": "var(--beta-foreground-soft)",
|
background: "var(--filter-background)",
|
||||||
|
},
|
||||||
|
beta: {
|
||||||
|
background: "var(--beta-background)",
|
||||||
|
foreground: "var(--beta-foreground)",
|
||||||
|
"foreground-soft": "var(--beta-foreground-soft)",
|
||||||
|
},
|
||||||
"chat-bot-icon": "var(--chat-bot-icon)",
|
"chat-bot-icon": "var(--chat-bot-icon)",
|
||||||
"chat-user-icon": "var(--chat-user-icon)",
|
"chat-user-icon": "var(--chat-user-icon)",
|
||||||
ice: "var(--ice)",
|
ice: "var(--ice)",
|
||||||
|
|
|
||||||
|
|
@ -54,28 +54,21 @@ test("user must see on handle hover a tooltip with possibility connections", asy
|
||||||
}
|
}
|
||||||
|
|
||||||
await visibleElementHandle.hover().then(async () => {
|
await visibleElementHandle.hover().then(async () => {
|
||||||
const testIds = [
|
await expect(
|
||||||
"available-output-inputs",
|
page.getByText("Drag to connect compatible inputs").first(),
|
||||||
"available-output-chains",
|
).toBeVisible();
|
||||||
"available-output-textsplitters",
|
|
||||||
"available-output-retrievers",
|
|
||||||
"available-output-prototypes",
|
|
||||||
"available-output-embeddings",
|
|
||||||
"available-output-data",
|
|
||||||
"available-output-vectorstores",
|
|
||||||
"available-output-memories",
|
|
||||||
"available-output-models",
|
|
||||||
"available-output-outputs",
|
|
||||||
"available-output-agents",
|
|
||||||
"available-output-helpers",
|
|
||||||
];
|
|
||||||
|
|
||||||
await Promise.all(
|
await expect(
|
||||||
testIds.map((id) => expect(page.getByTestId(id).first()).toBeVisible()),
|
page
|
||||||
);
|
.getByText("Select to filter compatible inputs and components")
|
||||||
|
.first(),
|
||||||
|
).toBeVisible();
|
||||||
|
|
||||||
await page.getByTestId("icon-X").click();
|
await expect(page.getByText("Output:").first()).toBeVisible();
|
||||||
await page.waitForTimeout(500);
|
|
||||||
|
await expect(
|
||||||
|
page.getByTestId("output-tooltip-message").first(),
|
||||||
|
).toBeVisible();
|
||||||
});
|
});
|
||||||
|
|
||||||
await page.getByTitle("fit view").click();
|
await page.getByTitle("fit view").click();
|
||||||
|
|
@ -96,13 +89,20 @@ test("user must see on handle hover a tooltip with possibility connections", asy
|
||||||
|
|
||||||
await visibleElementHandle.hover().then(async () => {
|
await visibleElementHandle.hover().then(async () => {
|
||||||
await expect(
|
await expect(
|
||||||
page.getByTestId("available-input-models").first(),
|
page.getByText("Drag to connect compatible outputs").first(),
|
||||||
).toBeVisible();
|
).toBeVisible();
|
||||||
await page.waitForTimeout(1000);
|
|
||||||
|
|
||||||
await page.getByTestId("icon-Search").click();
|
await expect(
|
||||||
|
page
|
||||||
|
.getByText("Select to filter compatible outputs and components")
|
||||||
|
.first(),
|
||||||
|
).toBeVisible();
|
||||||
|
|
||||||
await page.waitForTimeout(500);
|
await expect(page.getByText("Input:").first()).toBeVisible();
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
page.getByTestId("input-tooltip-languagemodel").first(),
|
||||||
|
).toBeVisible();
|
||||||
});
|
});
|
||||||
await page.getByTitle("fit view").click();
|
await page.getByTitle("fit view").click();
|
||||||
await page.getByTitle("zoom out").click();
|
await page.getByTitle("zoom out").click();
|
||||||
|
|
@ -121,16 +121,21 @@ test("user must see on handle hover a tooltip with possibility connections", asy
|
||||||
}
|
}
|
||||||
|
|
||||||
await visibleElementHandle.hover().then(async () => {
|
await visibleElementHandle.hover().then(async () => {
|
||||||
await page.waitForTimeout(2500);
|
|
||||||
|
|
||||||
await expect(
|
await expect(
|
||||||
page.getByTestId("available-input-retrievers").first(),
|
page.getByText("Drag to connect compatible outputs").first(),
|
||||||
).toBeVisible();
|
|
||||||
await expect(
|
|
||||||
page.getByTestId("available-input-vectorstores").first(),
|
|
||||||
).toBeVisible();
|
).toBeVisible();
|
||||||
|
|
||||||
await page.waitForTimeout(500);
|
await expect(
|
||||||
|
page
|
||||||
|
.getByText("Select to filter compatible outputs and components")
|
||||||
|
.first(),
|
||||||
|
).toBeVisible();
|
||||||
|
|
||||||
|
await expect(page.getByText("Input:").first()).toBeVisible();
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
page.getByTestId("input-tooltip-retriever").first(),
|
||||||
|
).toBeVisible();
|
||||||
});
|
});
|
||||||
|
|
||||||
await page.getByTitle("fit view").click();
|
await page.getByTitle("fit view").click();
|
||||||
|
|
@ -151,7 +156,19 @@ test("user must see on handle hover a tooltip with possibility connections", asy
|
||||||
|
|
||||||
await visibleElementHandle.hover().then(async () => {
|
await visibleElementHandle.hover().then(async () => {
|
||||||
await expect(
|
await expect(
|
||||||
page.getByTestId("available-input-helpers").first(),
|
page.getByText("Drag to connect compatible outputs").first(),
|
||||||
|
).toBeVisible();
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
page
|
||||||
|
.getByText("Select to filter compatible outputs and components")
|
||||||
|
.first(),
|
||||||
|
).toBeVisible();
|
||||||
|
|
||||||
|
await expect(page.getByText("Input:").first()).toBeVisible();
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
page.getByTestId("input-tooltip-basechatmemory").first(),
|
||||||
).toBeVisible();
|
).toBeVisible();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -54,99 +54,86 @@ test("user must see on handle click the possibility connections - LLMChain", asy
|
||||||
|
|
||||||
await page.getByTestId("handle-apirequest-shownode-urls-left").click();
|
await page.getByTestId("handle-apirequest-shownode-urls-left").click();
|
||||||
|
|
||||||
let disclosureTestIds = [
|
await page.waitForTimeout(500);
|
||||||
"disclosure-inputs",
|
|
||||||
"disclosure-outputs",
|
|
||||||
"disclosure-prompts",
|
|
||||||
"disclosure-models",
|
|
||||||
"disclosure-helpers",
|
|
||||||
"disclosure-agents",
|
|
||||||
"disclosure-chains",
|
|
||||||
"disclosure-prototypes",
|
|
||||||
];
|
|
||||||
|
|
||||||
let specificTestIds = [
|
expect(await page.getByTestId("icon-ListFilter")).toBeVisible();
|
||||||
"inputsChat Input",
|
|
||||||
"outputsChat Output",
|
|
||||||
"promptsPrompt",
|
|
||||||
"modelsAmazon Bedrock",
|
|
||||||
"helpersChat Memory",
|
|
||||||
"agentsCSVAgent",
|
|
||||||
"chainsConversationChain",
|
|
||||||
"prototypesConditional Router",
|
|
||||||
];
|
|
||||||
|
|
||||||
await Promise.all(
|
await page
|
||||||
disclosureTestIds.map((id) => expect(page.getByTestId(id)).toBeVisible()),
|
.getByTestId("icon-X")
|
||||||
);
|
.first()
|
||||||
|
.hover()
|
||||||
|
.then(async () => {
|
||||||
|
await page
|
||||||
|
.getByText("Remove filter", {
|
||||||
|
exact: false,
|
||||||
|
})
|
||||||
|
.first()
|
||||||
|
.isVisible();
|
||||||
|
});
|
||||||
|
|
||||||
await Promise.all(
|
await expect(page.getByTestId("disclosure-inputs")).toBeVisible();
|
||||||
specificTestIds.map((id) => expect(page.getByTestId(id)).toBeVisible()),
|
await expect(page.getByTestId("disclosure-outputs")).toBeVisible();
|
||||||
);
|
await expect(page.getByTestId("disclosure-prompts")).toBeVisible();
|
||||||
|
await expect(page.getByTestId("disclosure-models")).toBeVisible();
|
||||||
|
await expect(page.getByTestId("disclosure-helpers")).toBeVisible();
|
||||||
|
await expect(page.getByTestId("disclosure-agents")).toBeVisible();
|
||||||
|
await expect(page.getByTestId("disclosure-chains")).toBeVisible();
|
||||||
|
await expect(page.getByTestId("disclosure-prototypes")).toBeVisible();
|
||||||
|
|
||||||
|
await expect(page.getByTestId("inputsChat Input")).toBeVisible();
|
||||||
|
await expect(page.getByTestId("outputsChat Output")).toBeVisible();
|
||||||
|
await expect(page.getByTestId("promptsPrompt")).toBeVisible();
|
||||||
|
await expect(page.getByTestId("modelsAmazon Bedrock")).toBeVisible();
|
||||||
|
await expect(page.getByTestId("helpersChat Memory")).toBeVisible();
|
||||||
|
await expect(page.getByTestId("agentsCSVAgent")).toBeVisible();
|
||||||
|
await expect(page.getByTestId("chainsConversationChain")).toBeVisible();
|
||||||
|
await expect(page.getByTestId("prototypesConditional Router")).toBeVisible();
|
||||||
|
|
||||||
await page.getByPlaceholder("Search").click();
|
await page.getByPlaceholder("Search").click();
|
||||||
|
|
||||||
let notVisibleTestIds = [
|
await expect(page.getByTestId("inputsChat Input")).not.toBeVisible();
|
||||||
"inputsChat Input",
|
await expect(page.getByTestId("outputsChat Output")).not.toBeVisible();
|
||||||
"outputsChat Output",
|
await expect(page.getByTestId("promptsPrompt")).not.toBeVisible();
|
||||||
"promptsPrompt",
|
await expect(page.getByTestId("modelsAmazon Bedrock")).not.toBeVisible();
|
||||||
"modelsAmazon Bedrock",
|
await expect(page.getByTestId("helpersChat Memory")).not.toBeVisible();
|
||||||
"helpersChat Memory",
|
await expect(page.getByTestId("agentsTool Calling Agent")).not.toBeVisible();
|
||||||
"agentsTool Calling Agent",
|
await expect(page.getByTestId("chainsConversationChain")).not.toBeVisible();
|
||||||
"chainsConversationChain",
|
await expect(
|
||||||
"prototypesConditional Router",
|
page.getByTestId("prototypesConditional Router"),
|
||||||
];
|
).not.toBeVisible();
|
||||||
|
|
||||||
await Promise.all(
|
|
||||||
notVisibleTestIds.map((id) =>
|
|
||||||
expect(page.getByTestId(id)).not.toBeVisible(),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
await page.getByTestId("handle-apirequest-shownode-headers-left").click();
|
await page.getByTestId("handle-apirequest-shownode-headers-left").click();
|
||||||
|
|
||||||
disclosureTestIds = [
|
await expect(page.getByTestId("disclosure-data")).toBeVisible();
|
||||||
"disclosure-data",
|
await expect(page.getByTestId("disclosure-helpers")).toBeVisible();
|
||||||
"disclosure-helpers",
|
await expect(page.getByTestId("disclosure-vector stores")).toBeVisible();
|
||||||
"disclosure-vector stores",
|
await expect(page.getByTestId("disclosure-utilities")).toBeVisible();
|
||||||
"disclosure-utilities",
|
await expect(page.getByTestId("disclosure-prototypes")).toBeVisible();
|
||||||
"disclosure-prototypes",
|
await expect(page.getByTestId("disclosure-retrievers")).toBeVisible();
|
||||||
"disclosure-retrievers",
|
await expect(page.getByTestId("disclosure-embeddings")).toBeVisible();
|
||||||
"disclosure-tools",
|
await expect(page.getByTestId("disclosure-tools")).toBeVisible();
|
||||||
];
|
|
||||||
|
|
||||||
specificTestIds = [
|
await expect(page.getByTestId("dataAPI Request")).toBeVisible();
|
||||||
"dataAPI Request",
|
await expect(page.getByTestId("helpersChat Memory")).toBeVisible();
|
||||||
"helpersChat Memory",
|
await expect(page.getByTestId("vectorstoresAstra DB")).toBeVisible();
|
||||||
"vectorstoresAstra DB",
|
await expect(page.getByTestId("toolsSearch API")).toBeVisible();
|
||||||
"toolsSearch API",
|
await expect(page.getByTestId("prototypesSub Flow")).toBeVisible();
|
||||||
"prototypesSub Flow",
|
await expect(
|
||||||
"retrieversSelf Query Retriever",
|
page.getByTestId("retrieversSelf Query Retriever"),
|
||||||
];
|
).toBeVisible();
|
||||||
|
await expect(page.getByTestId("helpersSplit Text")).toBeVisible();
|
||||||
|
await expect(page.getByTestId("toolsSearch API")).toBeVisible();
|
||||||
|
|
||||||
await Promise.all(
|
await page.getByTestId("icon-X").first().click();
|
||||||
disclosureTestIds.map((id) => expect(page.getByTestId(id)).toBeVisible()),
|
|
||||||
);
|
|
||||||
|
|
||||||
await Promise.all(
|
await expect(page.getByTestId("dataAPI Request")).not.toBeVisible();
|
||||||
specificTestIds.map((id) => expect(page.getByTestId(id)).toBeVisible()),
|
await expect(page.getByTestId("helpersChat Memory")).not.toBeVisible();
|
||||||
);
|
await expect(page.getByTestId("vectorstoresAstra DB")).not.toBeVisible();
|
||||||
|
await expect(page.getByTestId("toolsSearch API")).not.toBeVisible();
|
||||||
await page.getByPlaceholder("Search").click();
|
await expect(page.getByTestId("prototypesSub Flow")).not.toBeVisible();
|
||||||
|
await expect(
|
||||||
notVisibleTestIds = [
|
page.getByTestId("retrieversSelf Query Retriever"),
|
||||||
"dataAPI Request",
|
).not.toBeVisible();
|
||||||
"helpersChat Memory",
|
await expect(page.getByTestId("helpersSplit Text")).not.toBeVisible();
|
||||||
"vectorstoresAstra DB",
|
await expect(page.getByTestId("toolsSearch API")).not.toBeVisible();
|
||||||
"toolsSearch API",
|
|
||||||
"prototypesSub Flow",
|
|
||||||
"retrieversSelf Query Retriever",
|
|
||||||
"textsplittersCharacterTextSplitter",
|
|
||||||
];
|
|
||||||
|
|
||||||
await Promise.all(
|
|
||||||
notVisibleTestIds.map((id) =>
|
|
||||||
expect(page.getByTestId(id)).not.toBeVisible(),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,4 @@
|
||||||
import { expect, test } from "@playwright/test";
|
import { expect, test } from "@playwright/test";
|
||||||
import * as dotenv from "dotenv";
|
|
||||||
import path from "path";
|
|
||||||
|
|
||||||
test("should be able to move flow from folder, rename it and be displayed on correct folder", async ({
|
test("should be able to move flow from folder, rename it and be displayed on correct folder", async ({
|
||||||
page,
|
page,
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import { expect, test } from "@playwright/test";
|
import { expect, test } from "@playwright/test";
|
||||||
|
|
||||||
test("should be able to see output preview from grouped components", async ({
|
test("should be able to see output preview from grouped components and connect components with a single click", async ({
|
||||||
page,
|
page,
|
||||||
}) => {
|
}) => {
|
||||||
await page.goto("/");
|
await page.goto("/");
|
||||||
|
|
@ -51,7 +51,7 @@ test("should be able to see output preview from grouped components", async ({
|
||||||
.hover()
|
.hover()
|
||||||
.then(async () => {
|
.then(async () => {
|
||||||
await page.mouse.down();
|
await page.mouse.down();
|
||||||
await page.mouse.move(-1000, 500);
|
await page.mouse.move(-600, 300);
|
||||||
await page.waitForTimeout(400);
|
await page.waitForTimeout(400);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -71,7 +71,7 @@ test("should be able to see output preview from grouped components", async ({
|
||||||
.hover()
|
.hover()
|
||||||
.then(async () => {
|
.then(async () => {
|
||||||
await page.mouse.down();
|
await page.mouse.down();
|
||||||
await page.mouse.move(-1000, 800);
|
await page.mouse.move(-600, 300);
|
||||||
await page.waitForTimeout(400);
|
await page.waitForTimeout(400);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -86,7 +86,7 @@ test("should be able to see output preview from grouped components", async ({
|
||||||
.hover()
|
.hover()
|
||||||
.then(async () => {
|
.then(async () => {
|
||||||
await page.mouse.down();
|
await page.mouse.down();
|
||||||
await page.mouse.move(-800, 800);
|
await page.mouse.move(-600, 300);
|
||||||
await page.waitForTimeout(400);
|
await page.waitForTimeout(400);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -101,7 +101,7 @@ test("should be able to see output preview from grouped components", async ({
|
||||||
.hover()
|
.hover()
|
||||||
.then(async () => {
|
.then(async () => {
|
||||||
await page.mouse.down();
|
await page.mouse.down();
|
||||||
await page.mouse.move(-200, 800);
|
await page.mouse.move(-600, 300);
|
||||||
await page.waitForTimeout(200);
|
await page.waitForTimeout(200);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -117,7 +117,7 @@ test("should be able to see output preview from grouped components", async ({
|
||||||
.hover()
|
.hover()
|
||||||
.then(async () => {
|
.then(async () => {
|
||||||
await page.mouse.down();
|
await page.mouse.down();
|
||||||
await page.mouse.move(-200, 500);
|
await page.mouse.move(-600, 300);
|
||||||
});
|
});
|
||||||
|
|
||||||
await page.mouse.up();
|
await page.mouse.up();
|
||||||
|
|
@ -134,13 +134,118 @@ test("should be able to see output preview from grouped components", async ({
|
||||||
const elementCombineTextOutput0 = await page
|
const elementCombineTextOutput0 = await page
|
||||||
.getByTestId("handle-combinetext-shownode-combined text-right")
|
.getByTestId("handle-combinetext-shownode-combined text-right")
|
||||||
.nth(0);
|
.nth(0);
|
||||||
await elementCombineTextOutput0.hover();
|
await elementCombineTextOutput0.click();
|
||||||
await page.mouse.down();
|
|
||||||
|
const blockedHandle = await page
|
||||||
|
.getByTestId("gradient-handle-textinput-shownode-text-right")
|
||||||
|
.nth(2);
|
||||||
|
const secondBlockedHandle = await page
|
||||||
|
.getByTestId("gradient-handle-combinetext-shownode-combined text-right")
|
||||||
|
.nth(2);
|
||||||
|
const thirdBlockedHandle = await page
|
||||||
|
.getByTestId("gradient-handle-textoutput-shownode-text-right")
|
||||||
|
.nth(0);
|
||||||
|
|
||||||
|
const hasGradient = await blockedHandle?.evaluate((el) => {
|
||||||
|
const style = window.getComputedStyle(el);
|
||||||
|
return (
|
||||||
|
style.backgroundImage.includes("conic-gradient") &&
|
||||||
|
style.backgroundImage.includes("rgb(203, 213, 225)")
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
await page.waitForTimeout(500);
|
||||||
|
|
||||||
|
const secondHasGradient = await secondBlockedHandle?.evaluate((el) => {
|
||||||
|
const style = window.getComputedStyle(el);
|
||||||
|
return (
|
||||||
|
style.backgroundImage.includes("conic-gradient") &&
|
||||||
|
style.backgroundImage.includes("rgb(203, 213, 225)")
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
await page.waitForTimeout(500);
|
||||||
|
|
||||||
|
const thirdHasGradient = await thirdBlockedHandle?.evaluate((el) => {
|
||||||
|
const style = window.getComputedStyle(el);
|
||||||
|
return (
|
||||||
|
style.backgroundImage.includes("conic-gradient") &&
|
||||||
|
style.backgroundImage.includes("rgb(203, 213, 225)")
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
await page.waitForTimeout(500);
|
||||||
|
|
||||||
|
expect(hasGradient).toBe(true);
|
||||||
|
expect(secondHasGradient).toBe(true);
|
||||||
|
expect(thirdHasGradient).toBe(true);
|
||||||
|
|
||||||
|
const unlockedHandle = await page
|
||||||
|
.getByTestId("gradient-handle-textinput-shownode-text-left")
|
||||||
|
.last();
|
||||||
|
const secondUnlockedHandle = await page
|
||||||
|
.getByTestId("gradient-handle-combinetext-shownode-second text-left")
|
||||||
|
.last();
|
||||||
|
const thirdUnlockedHandle = await page
|
||||||
|
.getByTestId("gradient-handle-combinetext-shownode-second text-left")
|
||||||
|
.first();
|
||||||
|
const fourthUnlockedHandle = await page
|
||||||
|
.getByTestId("gradient-handle-textoutput-shownode-text-left")
|
||||||
|
.first();
|
||||||
|
|
||||||
|
const hasGradientUnlocked = await unlockedHandle?.evaluate((el) => {
|
||||||
|
const style = window.getComputedStyle(el);
|
||||||
|
return (
|
||||||
|
style.backgroundImage.includes("conic-gradient") &&
|
||||||
|
style.backgroundImage.includes("rgb(79, 70, 229)")
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
await page.waitForTimeout(500);
|
||||||
|
|
||||||
|
const secondHasGradientUnlocked = await secondUnlockedHandle?.evaluate(
|
||||||
|
(el) => {
|
||||||
|
const style = window.getComputedStyle(el);
|
||||||
|
return (
|
||||||
|
style.backgroundImage.includes("conic-gradient") &&
|
||||||
|
style.backgroundImage.includes("rgb(79, 70, 229)")
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
await page.waitForTimeout(500);
|
||||||
|
|
||||||
|
const thirdHasGradientLocked = await thirdUnlockedHandle?.evaluate((el) => {
|
||||||
|
const style = window.getComputedStyle(el);
|
||||||
|
return (
|
||||||
|
style.backgroundImage.includes("conic-gradient") &&
|
||||||
|
style.backgroundImage.includes("rgb(203, 213, 225)")
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
await page.waitForTimeout(500);
|
||||||
|
|
||||||
|
const fourthHasGradientUnlocked = await fourthUnlockedHandle?.evaluate(
|
||||||
|
(el) => {
|
||||||
|
const style = window.getComputedStyle(el);
|
||||||
|
return (
|
||||||
|
style.backgroundImage.includes("conic-gradient") &&
|
||||||
|
style.backgroundImage.includes("rgb(79, 70, 229)")
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
await page.waitForTimeout(500);
|
||||||
|
|
||||||
|
expect(hasGradientUnlocked).toBe(true);
|
||||||
|
expect(secondHasGradientUnlocked).toBe(true);
|
||||||
|
expect(thirdHasGradientLocked).toBe(true);
|
||||||
|
expect(fourthHasGradientUnlocked).toBe(true);
|
||||||
|
|
||||||
const elementCombineTextInput1 = await page
|
const elementCombineTextInput1 = await page
|
||||||
.getByTestId("handle-combinetext-shownode-first text-left")
|
.getByTestId("handle-combinetext-shownode-first text-left")
|
||||||
.nth(1);
|
.nth(1);
|
||||||
await elementCombineTextInput1.hover();
|
await elementCombineTextInput1.click();
|
||||||
await page.mouse.up();
|
|
||||||
|
|
||||||
await page
|
await page
|
||||||
.getByTestId("title-Combine Text")
|
.getByTestId("title-Combine Text")
|
||||||
|
|
@ -157,68 +262,60 @@ test("should be able to see output preview from grouped components", async ({
|
||||||
const elementTextOutput0 = await page
|
const elementTextOutput0 = await page
|
||||||
.getByTestId("handle-textinput-shownode-text-right")
|
.getByTestId("handle-textinput-shownode-text-right")
|
||||||
.nth(0);
|
.nth(0);
|
||||||
await elementTextOutput0.hover();
|
await elementTextOutput0.click();
|
||||||
await page.mouse.down();
|
|
||||||
const elementGroupInput0 = await page.getByTestId(
|
const elementGroupInput0 = await page.getByTestId(
|
||||||
"handle-groupnode-shownode-first text-left",
|
"handle-groupnode-shownode-first text-left",
|
||||||
);
|
);
|
||||||
|
await elementGroupInput0.click();
|
||||||
await elementGroupInput0.hover();
|
|
||||||
await page.mouse.up();
|
|
||||||
|
|
||||||
//connection 3
|
//connection 3
|
||||||
const elementTextOutput1 = await page
|
const elementTextOutput1 = await page
|
||||||
.getByTestId("handle-textinput-shownode-text-right")
|
.getByTestId("handle-textinput-shownode-text-right")
|
||||||
.nth(2);
|
.nth(2);
|
||||||
await elementTextOutput1.hover();
|
await elementTextOutput1.click();
|
||||||
await page.mouse.down();
|
|
||||||
const elementGroupInput1 = await page
|
const elementGroupInput1 = await page
|
||||||
.getByTestId("handle-groupnode-shownode-second text-left")
|
.getByTestId("handle-groupnode-shownode-second text-left")
|
||||||
.nth(1);
|
.nth(1);
|
||||||
|
await elementGroupInput1.click();
|
||||||
await elementGroupInput1.hover();
|
|
||||||
await page.mouse.up();
|
|
||||||
|
|
||||||
//connection 4
|
//connection 4
|
||||||
const elementGroupOutput = await page
|
const elementGroupOutput = await page
|
||||||
.getByTestId("handle-groupnode-shownode-combined text-right")
|
.getByTestId("handle-groupnode-shownode-combined text-right")
|
||||||
.nth(0);
|
.nth(0);
|
||||||
await elementGroupOutput.hover();
|
await elementGroupOutput.click();
|
||||||
await page.mouse.down();
|
|
||||||
const elementTextOutputInput = await page
|
const elementTextOutputInput = await page
|
||||||
.getByTestId("handle-textoutput-shownode-text-left")
|
.getByTestId("handle-textoutput-shownode-text-left")
|
||||||
.nth(0);
|
.nth(0);
|
||||||
|
|
||||||
await elementTextOutputInput.hover();
|
await elementTextOutputInput.click();
|
||||||
await page.mouse.up();
|
|
||||||
|
|
||||||
await page.getByTestId("textarea_str_input_value").nth(0).fill(randomName);
|
await page.getByTestId("textarea_str_input_value").nth(0).fill(randomName);
|
||||||
|
|
||||||
await page.waitForTimeout(1000);
|
await page.waitForTimeout(500);
|
||||||
await page
|
await page
|
||||||
.getByTestId("textarea_str_input_value")
|
.getByTestId("textarea_str_input_value")
|
||||||
.nth(1)
|
.nth(1)
|
||||||
.fill(secondRandomName);
|
.fill(secondRandomName);
|
||||||
await page.waitForTimeout(1000);
|
await page.waitForTimeout(500);
|
||||||
|
|
||||||
await page
|
await page
|
||||||
.getByPlaceholder("Type something...", { exact: true })
|
.getByPlaceholder("Type something...", { exact: true })
|
||||||
.nth(6)
|
.nth(6)
|
||||||
.fill(thirdRandomName);
|
.fill(thirdRandomName);
|
||||||
await page.waitForTimeout(1000);
|
await page.waitForTimeout(500);
|
||||||
|
|
||||||
await page
|
await page
|
||||||
.getByPlaceholder("Type something...", { exact: true })
|
.getByPlaceholder("Type something...", { exact: true })
|
||||||
.nth(3)
|
.nth(3)
|
||||||
.fill("-");
|
.fill("-");
|
||||||
await page.waitForTimeout(1000);
|
await page.waitForTimeout(500);
|
||||||
|
|
||||||
await page
|
await page
|
||||||
.getByPlaceholder("Type something...", { exact: true })
|
.getByPlaceholder("Type something...", { exact: true })
|
||||||
.nth(4)
|
.nth(4)
|
||||||
.fill("-");
|
.fill("-");
|
||||||
|
|
||||||
await page.waitForTimeout(3000);
|
await page.waitForTimeout(500);
|
||||||
|
|
||||||
await page.getByTestId("button_run_text output").last().click();
|
await page.getByTestId("button_run_text output").last().click();
|
||||||
|
|
||||||
|
|
@ -227,13 +324,13 @@ test("should be able to see output preview from grouped components", async ({
|
||||||
await page.getByText("built successfully").last().click({
|
await page.getByText("built successfully").last().click({
|
||||||
timeout: 15000,
|
timeout: 15000,
|
||||||
});
|
});
|
||||||
await page.waitForTimeout(3000);
|
await page.waitForTimeout(500);
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
await page.getByTestId("output-inspection-combined text").first(),
|
await page.getByTestId("output-inspection-combined text").first(),
|
||||||
).not.toBeDisabled();
|
).not.toBeDisabled();
|
||||||
await page.getByTestId("output-inspection-combined text").first().click();
|
await page.getByTestId("output-inspection-combined text").first().click();
|
||||||
await page.waitForTimeout(1000);
|
await page.waitForTimeout(500);
|
||||||
|
|
||||||
await page.getByText("Component Output").isVisible();
|
await page.getByText("Component Output").isVisible();
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue