FN-7266: open simple workflow step inspectors

Restore simple workflow editor row selections so editable steps reveal their details consistently across compact and mobile views.

- Show the node inspector in desktop compact simple-editor mode when an inspectable row is selected.
- Keep mobile inspector collapse behavior intact while allowing subsequent row selections to reopen details.
- Cover row and pencil selection paths for mobile, custom, built-in, and end-node cases.
- Add a patch changeset for the published Fusion package.

Files changed:
 .changeset/fn-7266-simple-workflow-inspector.md    |  7 +++
 .../app/components/WorkflowNodeEditor.tsx          |  4 +-
 .../__tests__/MobileWorkflowGraphView.test.tsx     |  7 ++-
 .../__tests__/WorkflowNodeEditor.test.tsx          | 61 ++++++++++++++++++++--
 4 files changed, 73 insertions(+), 6 deletions(-)

Fusion-Task-Id: FN-7266

Fusion-Task-Lineage: 91e7f8d4-b56b-48a1-ad24-fb527c2730d4

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
This commit is contained in:
gsxdsm
2026-06-29 23:31:52 -07:00
parent eb83c81ffb
commit a0b35c13af
4 changed files with 73 additions and 6 deletions

View File

@@ -0,0 +1,7 @@
---
"@runfusion/fusion": patch
---
summary: Open Workflow simple-editor step details when rows are clicked.
category: fix
dev: Restores the simple graph row/pencil selection path for compact and mobile workflow editing.

View File

@@ -3566,9 +3566,9 @@ function InnerEditor({
)}
</section>
{/* FNXC:WorkflowSimpleEditor 2026-06-29-23:21: Simple editor row and pencil affordances must open the same node details in desktop compact and mobile presentations. Do not suppress this inspector only because the canvas is hidden; mobile collapse still closes the detail stage and end nodes remain non-inspectable. */}
{selectedNodeHasInspector &&
!(isMobileMode && inspectorCollapsed) &&
!(compactLayoutEnabled && !isMobileMode) && (
!(isMobileMode && inspectorCollapsed) && (
<aside className="wf-editor-inspector" data-testid="wf-node-inspector">
<div className="wf-inspector-heading">
{/* FNXC:WorkflowEditor 2026-06-21-10:00: Heading shows the node-kind title (from the help registry) so the pane names what is selected, falling back to the generic "Node" label. */}

View File

@@ -66,7 +66,7 @@ const rows: MobileWorkflowNodeSummary[] = [
];
describe("MobileWorkflowGraphView", () => {
it("renders graph rows and selects nodes and edges", () => {
it("renders graph rows and selects nodes, editable pencil rows, and edges", () => {
const onSelectNode = vi.fn();
const onSelectEdge = vi.fn();
render(
@@ -82,6 +82,11 @@ describe("MobileWorkflowGraphView", () => {
fireEvent.click(within(screen.getByTestId("mobile-wf-node-start")).getByRole("button", { name: /start/i }));
expect(onSelectNode).toHaveBeenCalledWith("start");
const promptMain = within(screen.getByTestId("mobile-wf-node-prompt")).getByRole("button", { name: /prompt/i });
expect(promptMain.querySelector("svg")).toBeInTheDocument();
fireEvent.click(promptMain);
expect(onSelectNode).toHaveBeenCalledWith("prompt");
fireEvent.click(screen.getByTestId("mobile-wf-edge-e1"));
expect(onSelectEdge).toHaveBeenCalledWith("e1");
});

View File

@@ -439,11 +439,10 @@ describe("workflow-flow-mapping", () => {
const failuresToEnd = edges.filter((edge) => edge.target === "end" && edge.data?.condition === "failure");
// FNXC:WorkflowOptionalGroup 2026-06-21-15:30: the coding built-in's pre-merge `workflow-step` seam was migrated to a `browser-verification` optional-group (U6), which now carries the failure->end edge in its place.
// FNXC:CodeReviewStep 2026-06-25-00:00: the default-on `code-review` optional-group is also on the pre-merge success path with its own failure->end edge (see builtin-code-review-group.test.ts), so it is an expected failure->end source too. This corrected a stale assertion that predated the code-review group's addition.
// FNXC:WorkflowPlanReview 2026-06-29-00:00: the built-in coding workflow now includes a `plan-review` gate on the normal path; its failure edge must remain renderable and independently clickable like the existing parallel failure-to-end edges.
// FNXC:WorkflowPlanReview 2026-06-29-23:18: FN-7265 removed the coding workflow's duplicate plan-review gate, so this renderability guard tracks the remaining failure-to-end sources without expecting a stale `plan-review` edge.
expect(failuresToEnd.map((edge) => edge.source).sort()).toEqual([
"execute",
"merge-attempt",
"plan-review",
"planning",
"review",
]);
@@ -900,6 +899,53 @@ describe("WorkflowNodeEditor", () => {
expect(await screen.findByTestId("wf-workflow-name")).toHaveTextContent("QA");
});
it("opens node details from the desktop compact simple editor row click", async () => {
vi.mocked(fetchWorkflows).mockResolvedValue([def()]);
render(<WorkflowNodeEditor isOpen onClose={() => {}} addToast={() => {}} />);
await screen.findByText("Save");
fireEvent.click(screen.getByTestId("wf-layout-toggle"));
const lintRow = await screen.findByTestId("mobile-wf-node-lint");
fireEvent.click(within(lintRow).getAllByRole("button")[0]);
const inspector = await screen.findByTestId("wf-node-inspector");
expect(within(inspector).getByLabelText("Prompt")).toBeInTheDocument();
expect(inspector.closest(".wf-editor-body")).not.toHaveClass("wf-editor-body--mobile-node-detail");
});
it("opens custom and built-in simple editor inspectors while leaving end nodes closed", async () => {
vi.mocked(fetchWorkflows).mockResolvedValue([v2Def(), builtinDef()]);
vi.mocked(fetchWorkflowPromptOverrides).mockResolvedValue({
stored: {},
effective: { execute: "Default execute prompt" },
defaults: { execute: "Default execute prompt" },
});
render(<WorkflowNodeEditor isOpen onClose={() => {}} addToast={() => {}} />);
await screen.findByText("Save");
fireEvent.click(screen.getByTestId("wf-layout-toggle"));
fireEvent.click(within(await screen.findByTestId("mobile-wf-node-start")).getAllByRole("button")[0]);
const startInspector = await screen.findByTestId("wf-node-inspector");
expect(within(startInspector).getByTestId("wf-start-inspector")).toBeInTheDocument();
expect(within(startInspector).queryByLabelText("Name")).not.toBeInTheDocument();
fireEvent.click(within(await screen.findByTestId("mobile-wf-node-end")).getAllByRole("button")[0]);
await waitFor(() => expect(screen.queryByTestId("wf-node-inspector")).not.toBeInTheDocument());
fireEvent.click(screen.getByRole("button", { name: "Default coding workflow" }));
await screen.findByTestId("wf-readonly-banner");
fireEvent.click(within((await screen.findAllByTestId("mobile-wf-node-execute"))[0]).getAllByRole("button")[0]);
const builtinInspector = await screen.findByTestId("wf-node-inspector");
const prompt = within(builtinInspector).getByLabelText("Prompt") as HTMLTextAreaElement;
expect(prompt).not.toHaveAttribute("readonly");
expect(prompt).toHaveValue("Default execute prompt");
expect(within(builtinInspector).getByText(/structure is read-only/i)).toBeInTheDocument();
});
it("collapses and expands the selected node inspector on mobile", async () => {
mockWorkflowEditorViewport("mobile");
vi.mocked(fetchWorkflows).mockResolvedValue([def()]);
@@ -922,8 +968,17 @@ describe("WorkflowNodeEditor", () => {
fireEvent.click(within(await screen.findByTestId("mobile-wf-node-lint")).getAllByRole("button")[0]);
expect(await screen.findByTestId("wf-node-inspector")).toBeInTheDocument();
const reopenedInspector = await screen.findByTestId("wf-node-inspector");
expect(reopenedInspector.closest(".wf-editor-body")).toHaveClass("wf-editor-body--mobile-node-detail");
expect(screen.getByTestId("wf-inspector-toggle")).toHaveAttribute("aria-expanded", "true");
fireEvent.click(screen.getByTestId("wf-inspector-toggle"));
await waitFor(() => expect(screen.queryByTestId("wf-node-inspector")).not.toBeInTheDocument());
fireEvent.click(within(await screen.findByTestId("mobile-wf-node-merge")).getAllByRole("button")[0]);
const nextInspector = await screen.findByTestId("wf-node-inspector");
expect(within(nextInspector).getByLabelText("Name")).toBeInTheDocument();
expect(nextInspector.closest(".wf-editor-body")).toHaveClass("wf-editor-body--mobile-node-detail");
});
it("edits the start node entry column from the desktop inspector and saves it", async () => {