fix: address console review findings

This commit is contained in:
lda
2026-07-03 08:27:36 +07:00 Verified
parent 47ac422965
commit b0b2cba7ab
23 changed files with 351 additions and 107 deletions
@@ -96,6 +96,18 @@ describe("WorkflowGraph", () => {
expect(onSelect).toHaveBeenCalledWith("review");
});
it("calls onNodeSelect when a focused node is activated by keyboard", () => {
const onSelect = vi.fn();
const { container } = render(<WorkflowGraph model={mockModel} onNodeSelect={onSelect} />);
const reviewNode = findNodeById(container, "review");
fireEvent.keyDown(reviewNode!, { key: "Enter" });
fireEvent.keyDown(reviewNode!, { key: " " });
expect(onSelect).toHaveBeenNthCalledWith(1, "review");
expect(onSelect).toHaveBeenNthCalledWith(2, "review");
});
it("highlights active node when activeNodeId is provided", () => {
const { container } = render(<WorkflowGraph model={mockModel} activeNodeId="review" />);
const reviewNode = findNodeById(container, "review");
+9 -3
View File
@@ -1,4 +1,4 @@
import { useCallback, useMemo } from "react";
import { useCallback, useMemo, type KeyboardEvent } from "react";
import {
ReactFlow,
Background,
@@ -39,10 +39,16 @@ const nodeColor = (data: WorkflowGraphNodeData): string => {
const CustomNode = ({ data, selected }: { data: WorkflowGraphNodeData; selected: boolean }) => {
const isActive = (data as WorkflowGraphNodeData & { isActive?: boolean }).isActive;
const handleKeyDown = (event: KeyboardEvent) => {
if (event.key !== "Enter" && event.key !== " ") return;
event.preventDefault();
data.onSelect?.(data.nodeId);
};
return (
<div
role="button"
tabIndex={0}
onKeyDown={handleKeyDown}
data-active={isActive}
data-node-id={data.nodeId}
className={`graph-node graph-node--${data.kind} ${selected ? "graph-node--selected" : ""} ${isActive ? "graph-node--active" : ""}`}
@@ -69,9 +75,9 @@ export const WorkflowGraph = ({ model, activeNodeId = null, onNodeSelect }: Work
id: n.id,
type: "custom",
position: n.position,
data: { ...n.data, isActive: activeNodeId === n.id },
data: { ...n.data, isActive: activeNodeId === n.id, onSelect: onNodeSelect },
})),
[model.nodes, activeNodeId],
[model.nodes, activeNodeId, onNodeSelect],
);
const edges: Edge[] = useMemo(
@@ -64,6 +64,24 @@ describe("buildWorkflowGraph", () => {
expect(openNode?.data.nodeRef).toBe("local.browser_click.open_click_page");
});
it("labels subgraph nodes from the workflow name", () => {
const model = buildWorkflowGraph({
nodes: [
{
id: "nested",
type: "subgraph",
workflow: "workflows.report.review",
input: [],
output: [],
},
],
edges: [],
});
expect(model.nodes[0]?.data.kind).toBe("subgraph");
expect(model.nodes[0]?.data.label).toBe("review");
});
it("creates edges from plan edges", () => {
const model = buildWorkflowGraph(samplePlan);
expect(model.edges.length).toBe(5);
@@ -15,6 +15,7 @@ export type WorkflowGraphNodeData = {
readonly label: string;
readonly nodeRef: string | null;
readonly raw: Readonly<Record<string, unknown>>;
readonly onSelect?: (nodeId: string) => void;
};
export type WorkflowGraphNode = {
@@ -66,6 +67,14 @@ const buildLabel = (node: Record<string, unknown>): string => {
if (type === "interrupt") return (node.kind as string) ?? "Interrupt";
if (type === "foreach") return "For Each";
if (type === "join") return "Join";
if (type === "subgraph") {
const workflowRef = node.workflow as string | undefined;
if (workflowRef) {
const parts = workflowRef.split(".");
return parts[parts.length - 1] ?? workflowRef;
}
return "Subgraph";
}
const nodeRef = node.node as string | undefined;
if (nodeRef) {
const parts = nodeRef.split(".");