FN-6165: fix builtin workflow prompt expansion

Keep builtin workflow prompts expandable while preserving read-only behavior.

- move the builtin-disabled fieldset so the prompt expand control stays clickable
- mark inline and fullscreen builtin prompt textareas read-only instead of disabling them
- add coverage for desktop and mobile builtin prompt expansion behavior in WorkflowNodeEditor

Files changed:
 .../app/components/WorkflowNodeEditor.tsx          |  7 +++
 .../__tests__/WorkflowNodeEditor.test.tsx          | 61 ++++++++++++++++++++++
 2 files changed, 68 insertions(+)

Fusion-Task-Id: FN-6165

Fusion-Task-Lineage: 02b1dd9f-6034-46a4-8790-75f1170ddb84
This commit is contained in:
gsxdsm
2026-06-09 22:49:35 -07:00
parent 7960789d37
commit 49cfa7fe88
2 changed files with 68 additions and 0 deletions

View File

@@ -2148,6 +2148,7 @@ function InnerEditor({
<textarea
rows={undefined}
value={selectedNodePromptValue}
readOnly={isBuiltin}
onChange={(e) => updateSelectedData({ config: { prompt: e.target.value } })}
autoFocus
/>
@@ -3176,6 +3177,7 @@ function InnerEditor({
onChange={(e) => updateSelectedData({ label: e.target.value })}
/>
</label>
</fieldset>
{selectedNode.data.kind === "prompt" || selectedNode.data.kind === "gate" ? (
<div className="wf-prompt-editor">
@@ -3184,9 +3186,13 @@ function InnerEditor({
<textarea
rows={5}
value={selectedNodePromptValue}
readOnly={isBuiltin}
onChange={(e) => updateSelectedData({ config: { prompt: e.target.value } })}
/>
</label>
{/* Expand button is outside <fieldset disabled={isBuiltin}> so it remains
clickable for builtin workflows. Root cause: HTML spec disables all
descendant buttons inside a disabled fieldset, including type="button". */}
<button
type="button"
className="btn btn-sm wf-prompt-expand-btn wf-prompt-expand-btn--inline"
@@ -3199,6 +3205,7 @@ function InnerEditor({
</div>
) : null}
<fieldset className="wf-inspector-fields" disabled={isBuiltin}>
{selectedNode.data.kind === "prompt" ? (
<>
<label className="wf-field">

View File

@@ -189,6 +189,15 @@ function builtinPrDef(): WorkflowDefinition {
};
}
async function selectBuiltinExecutePromptNode() {
await screen.findByTestId("wf-readonly-banner");
const promptNodes = await screen.findAllByTestId("wf-node-prompt");
const executeNode = promptNodes.find((node) => within(node).queryByText("Execute"));
expect(executeNode).toBeTruthy();
fireEvent.click(executeNode!);
return executeNode!;
}
function edgeRenderableAssertion(definition: WorkflowDefinition) {
const flow = irToFlow(definition);
const nodeIds = new Set(flow.nodes.map((node) => node.id));
@@ -643,6 +652,58 @@ describe("WorkflowNodeEditor", () => {
expect(getPromptFullscreenOverlay()).toBeNull();
});
it("shows a non-disabled expand button and read-only prompt for builtin workflow prompt nodes", async () => {
vi.mocked(fetchWorkflows).mockResolvedValue([builtinDef()]);
render(<WorkflowNodeEditor isOpen onClose={() => {}} addToast={() => {}} />);
await selectBuiltinExecutePromptNode();
const inspector = await screen.findByTestId("wf-node-inspector");
expect(within(inspector).getByLabelText("Prompt")).toHaveAttribute("readonly");
const expand = within(inspector).getByRole("button", { name: "Expand prompt editor" });
expect(expand).toBeInTheDocument();
expect(expand).not.toBeDisabled();
});
it("opens and collapses the fullscreen prompt editor for builtin workflows", async () => {
vi.mocked(fetchWorkflows).mockResolvedValue([builtinDef()]);
render(<WorkflowNodeEditor isOpen onClose={() => {}} addToast={() => {}} />);
await selectBuiltinExecutePromptNode();
fireEvent.click(await screen.findByRole("button", { name: "Expand prompt editor" }));
const fullscreenPromptEditor = getPromptFullscreenOverlay();
expect(fullscreenPromptEditor).toBeInTheDocument();
expect(fullscreenPromptEditor).toHaveClass("wf-prompt-editor--fullscreen");
expect(getPromptFullscreenTextarea()).toHaveAttribute("readonly");
fireEvent.click(within(fullscreenPromptEditor!).getByRole("button", { name: "Collapse prompt editor" }));
expect(getPromptFullscreenOverlay()).toBeNull();
});
it("opens and collapses the fullscreen prompt editor for builtin workflows on mobile", async () => {
mockWorkflowEditorViewport("mobile");
vi.mocked(fetchWorkflows).mockResolvedValue([builtinDef()]);
render(<WorkflowNodeEditor isOpen onClose={() => {}} addToast={() => {}} />);
fireEvent.click(await screen.findByRole("button", { name: "Default coding workflow" }));
await selectBuiltinExecutePromptNode();
const inspector = await screen.findByTestId("wf-node-inspector");
fireEvent.click(within(inspector).getByRole("button", { name: "Expand prompt editor" }));
const fullscreenPromptEditor = getPromptFullscreenOverlay();
expect(fullscreenPromptEditor).toBeInTheDocument();
expect(fullscreenPromptEditor).toHaveClass("wf-prompt-editor--fullscreen");
fireEvent.click(within(fullscreenPromptEditor!).getByRole("button", { name: "Collapse prompt editor" }));
expect(getPromptFullscreenOverlay()).toBeNull();
});
it("persists mobile fullscreen prompt edits back to the inline textarea", async () => {
mockWorkflowEditorViewport("mobile");
vi.mocked(fetchWorkflows).mockResolvedValue([v2Def()]);