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;
|
||||
}
|
||||
|
||||
.react-flow__edge.running .react-flow__edge-path {
|
||||
stroke: var(--status-blue) !important;
|
||||
}
|
||||
|
||||
.ag-react-container {
|
||||
width: 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 useFlowStore from "../../../../stores/flowStore";
|
||||
import { useTypesStore } from "../../../../stores/typesStore";
|
||||
import { NodeType } from "../../../../types/flow";
|
||||
import { groupByFamily } from "../../../../utils/utils";
|
||||
import TooltipRenderComponent from "../tooltipRenderComponent";
|
||||
import { convertTestName } from "@/components/storeCardComponent/utils/convert-test-name";
|
||||
|
||||
export default function HandleTooltips({
|
||||
left,
|
||||
export default function HandleTooltipComponent({
|
||||
isInput,
|
||||
tooltipTitle,
|
||||
colors,
|
||||
isConnecting,
|
||||
isCompatible,
|
||||
isSameNode,
|
||||
}: {
|
||||
left: boolean;
|
||||
nodes: NodeType[];
|
||||
isInput: boolean;
|
||||
colors: string[];
|
||||
tooltipTitle: string;
|
||||
isConnecting: boolean;
|
||||
isCompatible: boolean;
|
||||
isSameNode: boolean;
|
||||
}) {
|
||||
const myData = useTypesStore((state) => state.data);
|
||||
const nodes = useFlowStore((state) => state.nodes);
|
||||
|
||||
let groupedObj: any = groupByFamily(myData, tooltipTitle!, left, nodes!);
|
||||
|
||||
if (groupedObj && groupedObj.length > 0) {
|
||||
//@ts-ignore
|
||||
return groupedObj.map((item, index) => {
|
||||
return <TooltipRenderComponent index={index} item={item} left={left} />;
|
||||
});
|
||||
} else {
|
||||
//@ts-ignore
|
||||
return <span data-testid={`empty-tooltip-filter`}>{TOOLTIP_EMPTY}</span>;
|
||||
}
|
||||
const tooltips = tooltipTitle.split("\n");
|
||||
const plural = tooltips.length > 1 ? "s" : "";
|
||||
return (
|
||||
<div className="py-1.5 font-medium text-muted-foreground">
|
||||
{isSameNode ? (
|
||||
"Can't connect to the same node"
|
||||
) : (
|
||||
<div className="flex items-start gap-1.5">
|
||||
{isConnecting ? (
|
||||
isCompatible ? (
|
||||
<span>
|
||||
<span className="font-semibold text-foreground">Connect</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}
|
||||
showNode={showNode}
|
||||
testIdComplement={`${data?.type?.toLowerCase()}-${showNode ? "shownode" : "noshownode"}`}
|
||||
nodeId={data.id}
|
||||
/>
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -110,6 +110,7 @@ export default function NodeOutputField({
|
|||
id={id}
|
||||
title={title}
|
||||
edges={edges}
|
||||
nodeId={data.id}
|
||||
myData={myData}
|
||||
colors={colors}
|
||||
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 ShadTooltip from "../../../../components/shadTooltipComponent";
|
||||
import {
|
||||
|
|
@ -5,7 +8,7 @@ import {
|
|||
scapedJSONStringfy,
|
||||
} from "../../../../utils/reactflowUtils";
|
||||
import { classNames, cn, groupByFamily } from "../../../../utils/utils";
|
||||
import HandleTooltips from "../HandleTooltipComponent";
|
||||
import HandleTooltipComponent from "../HandleTooltipComponent";
|
||||
|
||||
export default function HandleRenderComponent({
|
||||
left,
|
||||
|
|
@ -20,6 +23,7 @@ export default function HandleRenderComponent({
|
|||
setFilterEdge,
|
||||
showNode,
|
||||
testIdComplement,
|
||||
nodeId,
|
||||
}: {
|
||||
left: boolean;
|
||||
nodes: any;
|
||||
|
|
@ -33,17 +37,168 @@ export default function HandleRenderComponent({
|
|||
setFilterEdge: any;
|
||||
showNode: any;
|
||||
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 (
|
||||
<div>
|
||||
<ShadTooltip
|
||||
open={openTooltip}
|
||||
setOpen={setOpenTooltip}
|
||||
styleClasses={"tooltip-fixed-width custom-scroll nowheel"}
|
||||
delayDuration={1000}
|
||||
content={
|
||||
<HandleTooltips
|
||||
left={left}
|
||||
nodes={nodes}
|
||||
tooltipTitle={tooltipTitle!}
|
||||
<HandleTooltipComponent
|
||||
isInput={left}
|
||||
colors={colors}
|
||||
tooltipTitle={tooltipTitle}
|
||||
isConnecting={!!filterPresent && !ownHandle}
|
||||
isCompatible={openHandle}
|
||||
isSameNode={sameNode && !ownHandle}
|
||||
/>
|
||||
}
|
||||
side={left ? "left" : "right"}
|
||||
|
|
@ -54,47 +209,73 @@ export default function HandleRenderComponent({
|
|||
}`}
|
||||
type={left ? "target" : "source"}
|
||||
position={left ? Position.Left : Position.Right}
|
||||
key={scapedJSONStringfy(proxy ? { ...id, proxy } : id)}
|
||||
id={scapedJSONStringfy(proxy ? { ...id, proxy } : id)}
|
||||
key={myId}
|
||||
id={myId}
|
||||
isValidConnection={(connection) =>
|
||||
isValidConnection(connection, nodes, edges)
|
||||
}
|
||||
className={classNames(
|
||||
left ? "-ml-0.5" : "-mr-0.5",
|
||||
"z-20 h-3 w-3 rounded-full border-none bg-background",
|
||||
`group/handle z-20 h-6 w-6 rounded-full border-none bg-transparent transition-all`,
|
||||
)}
|
||||
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={() => {
|
||||
setFilterEdge(groupByFamily(myData, tooltipTitle!, left, nodes!));
|
||||
setFilterType(currentFilter);
|
||||
if (filterOpenHandle && filterType) {
|
||||
onConnect(getConnection(filterType));
|
||||
setFilterType(undefined);
|
||||
setFilterEdge([]);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
onMouseUp={() => {
|
||||
setOpenTooltip(false);
|
||||
}}
|
||||
onContextMenu={(event) => {
|
||||
event.preventDefault();
|
||||
}}
|
||||
onMouseDown={(event) => {
|
||||
if (event.button === 0) {
|
||||
setHandleDragging(currentFilter);
|
||||
document.addEventListener("mouseup", handleMouseUp);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<div
|
||||
className={cn(
|
||||
"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",
|
||||
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
|
||||
className={cn(
|
||||
"absolute top-[50%] z-10 h-3 w-3 translate-y-[-50%] rounded-full bg-background",
|
||||
left ? "-left-[4px] -ml-0.5" : "-right-[4px] -mr-0.5",
|
||||
)}
|
||||
/>
|
||||
</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,
|
||||
styleClasses,
|
||||
delayDuration = 500,
|
||||
open,
|
||||
setOpen,
|
||||
}: ShadToolTipType): JSX.Element {
|
||||
return content ? (
|
||||
<Tooltip defaultOpen={!children} delayDuration={delayDuration}>
|
||||
<Tooltip
|
||||
defaultOpen={!children}
|
||||
open={open}
|
||||
onOpenChange={setOpen}
|
||||
delayDuration={delayDuration}
|
||||
>
|
||||
<TooltipTrigger asChild={asChild}>{children}</TooltipTrigger>
|
||||
<TooltipContent
|
||||
className={cn(styleClasses, "max-w-96")}
|
||||
className={cn("max-w-96", styleClasses)}
|
||||
side={side}
|
||||
avoidCollisions={false}
|
||||
sticky="always"
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import { DefaultEdge } from "@/CustomEdges";
|
||||
import NoteNode from "@/CustomNodes/NoteNode";
|
||||
import IconComponent from "@/components/genericIconComponent";
|
||||
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 { getNodeRenderType, isSupportedNodeTypes } from "@/utils/utils";
|
||||
|
||||
import { ENABLE_MVPS } from "@/customization/feature-flags";
|
||||
import _, { cloneDeep } from "lodash";
|
||||
import {
|
||||
KeyboardEvent,
|
||||
|
|
@ -457,6 +457,8 @@ export default function Page({ view }: { view?: boolean }): JSX.Element {
|
|||
onSelectionDragStart={onSelectionDragStart}
|
||||
onSelectionEnd={onSelectionEnd}
|
||||
onSelectionStart={onSelectionStart}
|
||||
connectionRadius={25}
|
||||
edgeTypes={{ default: DefaultEdge }}
|
||||
connectionLineComponent={ConnectionLineComponent}
|
||||
onDragOver={onDragOver}
|
||||
onNodeDragStop={onNodeDragStop}
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ export default function ParentDisclosureComponent({
|
|||
data-testid={testId}
|
||||
>
|
||||
<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 && (
|
||||
<div className="h-fit rounded-full bg-beta-background px-2 py-1 text-xs/3 font-semibold text-beta-foreground-soft">
|
||||
BETA
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ import { nodeIconsLucide } from "../../../../utils/styleUtils";
|
|||
import ParentDisclosureComponent from "../ParentDisclosureComponent";
|
||||
import { SidebarCategoryComponent } from "./SidebarCategoryComponent";
|
||||
|
||||
import { SidebarFilterComponent } from "./sidebarFilterComponent";
|
||||
import { sortKeys } from "./utils";
|
||||
|
||||
export default function ExtraSidebar(): JSX.Element {
|
||||
|
|
@ -25,6 +26,7 @@ export default function ExtraSidebar(): JSX.Element {
|
|||
const getFilterEdge = useFlowStore((state) => state.getFilterEdge);
|
||||
const setFilterEdge = useFlowStore((state) => state.setFilterEdge);
|
||||
const hasStore = useStoreStore((state) => state.hasStore);
|
||||
const filterType = useFlowStore((state) => state.filterType);
|
||||
|
||||
const setErrorData = useAlertStore((state) => state.setErrorData);
|
||||
const [dataFilter, setFilterData] = useState(data);
|
||||
|
|
@ -222,8 +224,18 @@ export default function ExtraSidebar(): JSX.Element {
|
|||
|
||||
<div className="side-bar-components-div-arrangement">
|
||||
<div className="parent-disclosure-arrangement">
|
||||
<div className="flex items-center gap-4 align-middle">
|
||||
<span className="parent-disclosure-title">Components</span>
|
||||
<div className="flex w-full flex-col items-start justify-between gap-2.5">
|
||||
<span className="text-sm font-medium">Components</span>
|
||||
{filterType && (
|
||||
<SidebarFilterComponent
|
||||
isInput={!!filterType.source}
|
||||
type={filterType.type}
|
||||
resetFilters={() => {
|
||||
setFilterEdge([]);
|
||||
setFilterData(data);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<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) => {
|
||||
if (newState.length === 0) {
|
||||
set({ filterType: undefined });
|
||||
}
|
||||
set({ getFilterEdge: newState });
|
||||
},
|
||||
getFilterEdge: [],
|
||||
|
|
@ -469,8 +472,6 @@ const useFlowStore = create<FlowStoreType>((set, get) => ({
|
|||
targetHandle: scapeJSONParse(connection.targetHandle!),
|
||||
sourceHandle: scapeJSONParse(connection.sourceHandle!),
|
||||
},
|
||||
// style: { stroke: "#555" },
|
||||
// className: "stroke-foreground stroke-connection",
|
||||
},
|
||||
oldEdges,
|
||||
);
|
||||
|
|
@ -528,6 +529,7 @@ const useFlowStore = create<FlowStoreType>((set, get) => ({
|
|||
get().updateBuildStatus(ids, BuildStatus.ERROR);
|
||||
throw new Error("Invalid components");
|
||||
}
|
||||
get().updateEdgesRunningByNodes(nodes, true);
|
||||
}
|
||||
function handleBuildUpdate(
|
||||
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().setLockChat(false);
|
||||
},
|
||||
|
|
@ -653,6 +659,10 @@ const useFlowStore = create<FlowStoreType>((set, get) => ({
|
|||
title:
|
||||
"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 });
|
||||
get().setIsBuilding(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
|
||||
.map((element) => element.reference)
|
||||
.filter(Boolean) as string[];
|
||||
useFlowStore.getState().updateBuildStatus(idList, BuildStatus.BUILDING);
|
||||
get().updateBuildStatus(idList, BuildStatus.BUILDING);
|
||||
},
|
||||
onValidateNodes: validateSubgraph,
|
||||
nodes: get().nodes || undefined,
|
||||
|
|
@ -680,6 +690,17 @@ const useFlowStore = create<FlowStoreType>((set, get) => ({
|
|||
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: (
|
||||
vertices: {
|
||||
verticesIds: string[];
|
||||
|
|
@ -762,6 +783,15 @@ const useFlowStore = create<FlowStoreType>((set, get) => ({
|
|||
setBuildController: (controller) => {
|
||||
set({ buildController: controller });
|
||||
},
|
||||
handleDragging: undefined,
|
||||
setHandleDragging: (handleDragging) => {
|
||||
set({ handleDragging });
|
||||
},
|
||||
|
||||
filterType: undefined,
|
||||
setFilterType: (filterType) => {
|
||||
set({ filterType });
|
||||
},
|
||||
}));
|
||||
|
||||
export default useFlowStore;
|
||||
|
|
|
|||
|
|
@ -231,7 +231,7 @@
|
|||
@apply fill-chat-trigger-disabled stroke-chat-trigger-disabled stroke-1;
|
||||
}
|
||||
.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 {
|
||||
@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 */
|
||||
@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 {
|
||||
@apply flex items-center text-sm text-primary;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -169,6 +169,26 @@ textarea[class^="ag-"]:focus {
|
|||
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) {
|
||||
z-index: -1 !important;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -57,6 +57,8 @@
|
|||
--hover: #f2f4f5;
|
||||
--disabled-run: #6366f1;
|
||||
|
||||
--filter-foreground: #4f46e5;
|
||||
--filter-background: #eef2ff;
|
||||
/* Colors that are shared in dark and light mode */
|
||||
--blur-shared: #151923de;
|
||||
--build-trigger: #dc735b;
|
||||
|
|
@ -114,6 +116,9 @@
|
|||
--destructive: 0 60% 25%; /* hsl(0 60% 25%) */
|
||||
--destructive-foreground: 210 40% 98%; /* hsl(210 40% 98%) */
|
||||
|
||||
--filter-foreground: #eef2ff;
|
||||
--filter-background: #4e46e599;
|
||||
|
||||
--ring: 216 24% 30%; /* hsl(216 24% 30%) */
|
||||
|
||||
--radius: 0.5rem;
|
||||
|
|
|
|||
|
|
@ -339,6 +339,8 @@ export type ShadTooltipProps = {
|
|||
style?: string;
|
||||
};
|
||||
export type ShadToolTipType = {
|
||||
open?: boolean;
|
||||
setOpen?: (open: boolean) => void;
|
||||
content?: ReactNode | null;
|
||||
side?: "top" | "right" | "bottom" | "left";
|
||||
asChild?: boolean;
|
||||
|
|
|
|||
|
|
@ -180,6 +180,52 @@ export type FlowStoreType = {
|
|||
edges?: Edge[];
|
||||
viewport?: Viewport;
|
||||
}) => 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;
|
||||
buildController: AbortController;
|
||||
setBuildController: (controller: AbortController) => void;
|
||||
|
|
|
|||
|
|
@ -215,6 +215,9 @@ export function isValidConnection(
|
|||
nodes: Node[],
|
||||
edges: Edge[],
|
||||
) {
|
||||
if (source === target) {
|
||||
return false;
|
||||
}
|
||||
const targetHandleObject: targetHandleType = scapeJSONParse(targetHandle!);
|
||||
const sourceHandleObject: sourceHandleType = scapeJSONParse(sourceHandle!);
|
||||
if (
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import {
|
|||
AlertTriangle,
|
||||
ArrowBigUp,
|
||||
ArrowLeft,
|
||||
ArrowRight,
|
||||
ArrowUpToLine,
|
||||
Bell,
|
||||
Binary,
|
||||
|
|
@ -63,6 +64,7 @@ import {
|
|||
FileText,
|
||||
FileType2,
|
||||
FileUp,
|
||||
Filter,
|
||||
FlaskConical,
|
||||
FolderIcon,
|
||||
FolderPlus,
|
||||
|
|
@ -87,6 +89,7 @@ import {
|
|||
Layers,
|
||||
Link,
|
||||
Link2,
|
||||
ListFilter,
|
||||
Loader2,
|
||||
Lock,
|
||||
LogIn,
|
||||
|
|
@ -401,10 +404,12 @@ export const nodeIconsLucide: iconsType = {
|
|||
GoogleSearchRun: GoogleIcon,
|
||||
Google: GoogleIcon,
|
||||
GoogleGenerativeAI: GoogleGenerativeAIIcon,
|
||||
ArrowRight,
|
||||
Groq: GroqIcon,
|
||||
HCD: HCDIcon,
|
||||
HNLoader: HackerNewsIcon,
|
||||
Unstructured: UnstructuredIcon,
|
||||
Filter: Filter,
|
||||
HuggingFaceHub: HuggingFaceIcon,
|
||||
HuggingFace: HuggingFaceIcon,
|
||||
HuggingFaceEmbeddings: HuggingFaceIcon,
|
||||
|
|
@ -414,6 +419,7 @@ export const nodeIconsLucide: iconsType = {
|
|||
Meta: MetaIcon,
|
||||
CheckCheck,
|
||||
Midjorney: MidjourneyIcon,
|
||||
ListFilter,
|
||||
MongoDBAtlasVectorSearch: MongoDBIcon,
|
||||
MongoDB: MongoDBIcon,
|
||||
MongoDBChatMessageHistory: MongoDBIcon,
|
||||
|
|
|
|||
|
|
@ -97,9 +97,15 @@ const config = {
|
|||
"status-gray": "var(--status-gray)",
|
||||
"success-background": "var(--success-background)",
|
||||
"success-foreground": "var(--success-foreground)",
|
||||
"beta-background": "var(--beta-background)",
|
||||
"beta-foreground": "var(--beta-foreground)",
|
||||
"beta-foreground-soft": "var(--beta-foreground-soft)",
|
||||
filter: {
|
||||
foreground: "var(--filter-foreground)",
|
||||
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-user-icon": "var(--chat-user-icon)",
|
||||
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 () => {
|
||||
const testIds = [
|
||||
"available-output-inputs",
|
||||
"available-output-chains",
|
||||
"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 expect(
|
||||
page.getByText("Drag to connect compatible inputs").first(),
|
||||
).toBeVisible();
|
||||
|
||||
await Promise.all(
|
||||
testIds.map((id) => expect(page.getByTestId(id).first()).toBeVisible()),
|
||||
);
|
||||
await expect(
|
||||
page
|
||||
.getByText("Select to filter compatible inputs and components")
|
||||
.first(),
|
||||
).toBeVisible();
|
||||
|
||||
await page.getByTestId("icon-X").click();
|
||||
await page.waitForTimeout(500);
|
||||
await expect(page.getByText("Output:").first()).toBeVisible();
|
||||
|
||||
await expect(
|
||||
page.getByTestId("output-tooltip-message").first(),
|
||||
).toBeVisible();
|
||||
});
|
||||
|
||||
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 expect(
|
||||
page.getByTestId("available-input-models").first(),
|
||||
page.getByText("Drag to connect compatible outputs").first(),
|
||||
).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("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 page.waitForTimeout(2500);
|
||||
|
||||
await expect(
|
||||
page.getByTestId("available-input-retrievers").first(),
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
page.getByTestId("available-input-vectorstores").first(),
|
||||
page.getByText("Drag to connect compatible outputs").first(),
|
||||
).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();
|
||||
|
|
@ -151,7 +156,19 @@ test("user must see on handle hover a tooltip with possibility connections", asy
|
|||
|
||||
await visibleElementHandle.hover().then(async () => {
|
||||
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();
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
||||
let disclosureTestIds = [
|
||||
"disclosure-inputs",
|
||||
"disclosure-outputs",
|
||||
"disclosure-prompts",
|
||||
"disclosure-models",
|
||||
"disclosure-helpers",
|
||||
"disclosure-agents",
|
||||
"disclosure-chains",
|
||||
"disclosure-prototypes",
|
||||
];
|
||||
await page.waitForTimeout(500);
|
||||
|
||||
let specificTestIds = [
|
||||
"inputsChat Input",
|
||||
"outputsChat Output",
|
||||
"promptsPrompt",
|
||||
"modelsAmazon Bedrock",
|
||||
"helpersChat Memory",
|
||||
"agentsCSVAgent",
|
||||
"chainsConversationChain",
|
||||
"prototypesConditional Router",
|
||||
];
|
||||
expect(await page.getByTestId("icon-ListFilter")).toBeVisible();
|
||||
|
||||
await Promise.all(
|
||||
disclosureTestIds.map((id) => expect(page.getByTestId(id)).toBeVisible()),
|
||||
);
|
||||
await page
|
||||
.getByTestId("icon-X")
|
||||
.first()
|
||||
.hover()
|
||||
.then(async () => {
|
||||
await page
|
||||
.getByText("Remove filter", {
|
||||
exact: false,
|
||||
})
|
||||
.first()
|
||||
.isVisible();
|
||||
});
|
||||
|
||||
await Promise.all(
|
||||
specificTestIds.map((id) => expect(page.getByTestId(id)).toBeVisible()),
|
||||
);
|
||||
await expect(page.getByTestId("disclosure-inputs")).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();
|
||||
|
||||
let notVisibleTestIds = [
|
||||
"inputsChat Input",
|
||||
"outputsChat Output",
|
||||
"promptsPrompt",
|
||||
"modelsAmazon Bedrock",
|
||||
"helpersChat Memory",
|
||||
"agentsTool Calling Agent",
|
||||
"chainsConversationChain",
|
||||
"prototypesConditional Router",
|
||||
];
|
||||
|
||||
await Promise.all(
|
||||
notVisibleTestIds.map((id) =>
|
||||
expect(page.getByTestId(id)).not.toBeVisible(),
|
||||
),
|
||||
);
|
||||
await expect(page.getByTestId("inputsChat Input")).not.toBeVisible();
|
||||
await expect(page.getByTestId("outputsChat Output")).not.toBeVisible();
|
||||
await expect(page.getByTestId("promptsPrompt")).not.toBeVisible();
|
||||
await expect(page.getByTestId("modelsAmazon Bedrock")).not.toBeVisible();
|
||||
await expect(page.getByTestId("helpersChat Memory")).not.toBeVisible();
|
||||
await expect(page.getByTestId("agentsTool Calling Agent")).not.toBeVisible();
|
||||
await expect(page.getByTestId("chainsConversationChain")).not.toBeVisible();
|
||||
await expect(
|
||||
page.getByTestId("prototypesConditional Router"),
|
||||
).not.toBeVisible();
|
||||
|
||||
await page.getByTestId("handle-apirequest-shownode-headers-left").click();
|
||||
|
||||
disclosureTestIds = [
|
||||
"disclosure-data",
|
||||
"disclosure-helpers",
|
||||
"disclosure-vector stores",
|
||||
"disclosure-utilities",
|
||||
"disclosure-prototypes",
|
||||
"disclosure-retrievers",
|
||||
"disclosure-tools",
|
||||
];
|
||||
await expect(page.getByTestId("disclosure-data")).toBeVisible();
|
||||
await expect(page.getByTestId("disclosure-helpers")).toBeVisible();
|
||||
await expect(page.getByTestId("disclosure-vector stores")).toBeVisible();
|
||||
await expect(page.getByTestId("disclosure-utilities")).toBeVisible();
|
||||
await expect(page.getByTestId("disclosure-prototypes")).toBeVisible();
|
||||
await expect(page.getByTestId("disclosure-retrievers")).toBeVisible();
|
||||
await expect(page.getByTestId("disclosure-embeddings")).toBeVisible();
|
||||
await expect(page.getByTestId("disclosure-tools")).toBeVisible();
|
||||
|
||||
specificTestIds = [
|
||||
"dataAPI Request",
|
||||
"helpersChat Memory",
|
||||
"vectorstoresAstra DB",
|
||||
"toolsSearch API",
|
||||
"prototypesSub Flow",
|
||||
"retrieversSelf Query Retriever",
|
||||
];
|
||||
await expect(page.getByTestId("dataAPI Request")).toBeVisible();
|
||||
await expect(page.getByTestId("helpersChat Memory")).toBeVisible();
|
||||
await expect(page.getByTestId("vectorstoresAstra DB")).toBeVisible();
|
||||
await expect(page.getByTestId("toolsSearch API")).toBeVisible();
|
||||
await expect(page.getByTestId("prototypesSub Flow")).toBeVisible();
|
||||
await expect(
|
||||
page.getByTestId("retrieversSelf Query Retriever"),
|
||||
).toBeVisible();
|
||||
await expect(page.getByTestId("helpersSplit Text")).toBeVisible();
|
||||
await expect(page.getByTestId("toolsSearch API")).toBeVisible();
|
||||
|
||||
await Promise.all(
|
||||
disclosureTestIds.map((id) => expect(page.getByTestId(id)).toBeVisible()),
|
||||
);
|
||||
await page.getByTestId("icon-X").first().click();
|
||||
|
||||
await Promise.all(
|
||||
specificTestIds.map((id) => expect(page.getByTestId(id)).toBeVisible()),
|
||||
);
|
||||
|
||||
await page.getByPlaceholder("Search").click();
|
||||
|
||||
notVisibleTestIds = [
|
||||
"dataAPI Request",
|
||||
"helpersChat Memory",
|
||||
"vectorstoresAstra DB",
|
||||
"toolsSearch API",
|
||||
"prototypesSub Flow",
|
||||
"retrieversSelf Query Retriever",
|
||||
"textsplittersCharacterTextSplitter",
|
||||
];
|
||||
|
||||
await Promise.all(
|
||||
notVisibleTestIds.map((id) =>
|
||||
expect(page.getByTestId(id)).not.toBeVisible(),
|
||||
),
|
||||
);
|
||||
await expect(page.getByTestId("dataAPI Request")).not.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 expect(page.getByTestId("prototypesSub Flow")).not.toBeVisible();
|
||||
await expect(
|
||||
page.getByTestId("retrieversSelf Query Retriever"),
|
||||
).not.toBeVisible();
|
||||
await expect(page.getByTestId("helpersSplit Text")).not.toBeVisible();
|
||||
await expect(page.getByTestId("toolsSearch API")).not.toBeVisible();
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,6 +1,4 @@
|
|||
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 ({
|
||||
page,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
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,
|
||||
}) => {
|
||||
await page.goto("/");
|
||||
|
|
@ -51,7 +51,7 @@ test("should be able to see output preview from grouped components", async ({
|
|||
.hover()
|
||||
.then(async () => {
|
||||
await page.mouse.down();
|
||||
await page.mouse.move(-1000, 500);
|
||||
await page.mouse.move(-600, 300);
|
||||
await page.waitForTimeout(400);
|
||||
});
|
||||
|
||||
|
|
@ -71,7 +71,7 @@ test("should be able to see output preview from grouped components", async ({
|
|||
.hover()
|
||||
.then(async () => {
|
||||
await page.mouse.down();
|
||||
await page.mouse.move(-1000, 800);
|
||||
await page.mouse.move(-600, 300);
|
||||
await page.waitForTimeout(400);
|
||||
});
|
||||
|
||||
|
|
@ -86,7 +86,7 @@ test("should be able to see output preview from grouped components", async ({
|
|||
.hover()
|
||||
.then(async () => {
|
||||
await page.mouse.down();
|
||||
await page.mouse.move(-800, 800);
|
||||
await page.mouse.move(-600, 300);
|
||||
await page.waitForTimeout(400);
|
||||
});
|
||||
|
||||
|
|
@ -101,7 +101,7 @@ test("should be able to see output preview from grouped components", async ({
|
|||
.hover()
|
||||
.then(async () => {
|
||||
await page.mouse.down();
|
||||
await page.mouse.move(-200, 800);
|
||||
await page.mouse.move(-600, 300);
|
||||
await page.waitForTimeout(200);
|
||||
});
|
||||
|
||||
|
|
@ -117,7 +117,7 @@ test("should be able to see output preview from grouped components", async ({
|
|||
.hover()
|
||||
.then(async () => {
|
||||
await page.mouse.down();
|
||||
await page.mouse.move(-200, 500);
|
||||
await page.mouse.move(-600, 300);
|
||||
});
|
||||
|
||||
await page.mouse.up();
|
||||
|
|
@ -134,13 +134,118 @@ test("should be able to see output preview from grouped components", async ({
|
|||
const elementCombineTextOutput0 = await page
|
||||
.getByTestId("handle-combinetext-shownode-combined text-right")
|
||||
.nth(0);
|
||||
await elementCombineTextOutput0.hover();
|
||||
await page.mouse.down();
|
||||
await elementCombineTextOutput0.click();
|
||||
|
||||
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
|
||||
.getByTestId("handle-combinetext-shownode-first text-left")
|
||||
.nth(1);
|
||||
await elementCombineTextInput1.hover();
|
||||
await page.mouse.up();
|
||||
await elementCombineTextInput1.click();
|
||||
|
||||
await page
|
||||
.getByTestId("title-Combine Text")
|
||||
|
|
@ -157,68 +262,60 @@ test("should be able to see output preview from grouped components", async ({
|
|||
const elementTextOutput0 = await page
|
||||
.getByTestId("handle-textinput-shownode-text-right")
|
||||
.nth(0);
|
||||
await elementTextOutput0.hover();
|
||||
await page.mouse.down();
|
||||
await elementTextOutput0.click();
|
||||
const elementGroupInput0 = await page.getByTestId(
|
||||
"handle-groupnode-shownode-first text-left",
|
||||
);
|
||||
|
||||
await elementGroupInput0.hover();
|
||||
await page.mouse.up();
|
||||
await elementGroupInput0.click();
|
||||
|
||||
//connection 3
|
||||
const elementTextOutput1 = await page
|
||||
.getByTestId("handle-textinput-shownode-text-right")
|
||||
.nth(2);
|
||||
await elementTextOutput1.hover();
|
||||
await page.mouse.down();
|
||||
await elementTextOutput1.click();
|
||||
const elementGroupInput1 = await page
|
||||
.getByTestId("handle-groupnode-shownode-second text-left")
|
||||
.nth(1);
|
||||
|
||||
await elementGroupInput1.hover();
|
||||
await page.mouse.up();
|
||||
await elementGroupInput1.click();
|
||||
|
||||
//connection 4
|
||||
const elementGroupOutput = await page
|
||||
.getByTestId("handle-groupnode-shownode-combined text-right")
|
||||
.nth(0);
|
||||
await elementGroupOutput.hover();
|
||||
await page.mouse.down();
|
||||
await elementGroupOutput.click();
|
||||
const elementTextOutputInput = await page
|
||||
.getByTestId("handle-textoutput-shownode-text-left")
|
||||
.nth(0);
|
||||
|
||||
await elementTextOutputInput.hover();
|
||||
await page.mouse.up();
|
||||
await elementTextOutputInput.click();
|
||||
|
||||
await page.getByTestId("textarea_str_input_value").nth(0).fill(randomName);
|
||||
|
||||
await page.waitForTimeout(1000);
|
||||
await page.waitForTimeout(500);
|
||||
await page
|
||||
.getByTestId("textarea_str_input_value")
|
||||
.nth(1)
|
||||
.fill(secondRandomName);
|
||||
await page.waitForTimeout(1000);
|
||||
await page.waitForTimeout(500);
|
||||
|
||||
await page
|
||||
.getByPlaceholder("Type something...", { exact: true })
|
||||
.nth(6)
|
||||
.fill(thirdRandomName);
|
||||
await page.waitForTimeout(1000);
|
||||
await page.waitForTimeout(500);
|
||||
|
||||
await page
|
||||
.getByPlaceholder("Type something...", { exact: true })
|
||||
.nth(3)
|
||||
.fill("-");
|
||||
await page.waitForTimeout(1000);
|
||||
await page.waitForTimeout(500);
|
||||
|
||||
await page
|
||||
.getByPlaceholder("Type something...", { exact: true })
|
||||
.nth(4)
|
||||
.fill("-");
|
||||
|
||||
await page.waitForTimeout(3000);
|
||||
await page.waitForTimeout(500);
|
||||
|
||||
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({
|
||||
timeout: 15000,
|
||||
});
|
||||
await page.waitForTimeout(3000);
|
||||
await page.waitForTimeout(500);
|
||||
|
||||
expect(
|
||||
await page.getByTestId("output-inspection-combined text").first(),
|
||||
).not.toBeDisabled();
|
||||
await page.getByTestId("output-inspection-combined text").first().click();
|
||||
await page.waitForTimeout(1000);
|
||||
await page.waitForTimeout(500);
|
||||
|
||||
await page.getByText("Component Output").isVisible();
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue