fix: make mobile workflow node details full height

This commit is contained in:
gsxdsm
2026-06-10 08:37:01 -07:00
parent ae63cd9008
commit f7ae18a49f
4 changed files with 49 additions and 9 deletions

View File

@@ -1705,4 +1705,21 @@
border-top: 1px solid var(--border); border-top: 1px solid var(--border);
overflow-y: auto; overflow-y: auto;
} }
.wf-editor-body--mobile-node-detail .wf-editor-canvas-wrap {
display: none;
min-height: 0;
}
.wf-editor-body--mobile-node-detail .wf-editor-inspector {
display: flex;
flex: 1 1 auto;
width: 100%;
min-width: 0;
min-height: 0;
max-height: none;
border-left: none;
border-top: none;
overflow-y: auto;
}
} }

View File

@@ -1879,6 +1879,11 @@ function InnerEditor({
}, [nodes, unplaced, serverNodeError, t]); }, [nodes, unplaced, serverNodeError, t]);
const selectedNode = nodes.find((n) => n.id === selectedNodeId) ?? null; const selectedNode = nodes.find((n) => n.id === selectedNodeId) ?? null;
const selectedNodeHasInspector =
selectedNode !== null &&
selectedNode.data.kind !== "start" &&
selectedNode.data.kind !== "end";
const mobileNodeDetailStage = isMobileViewport && selectedNodeHasInspector && !inspectorCollapsed;
const selectedEdge = edges.find((e) => e.id === selectedEdgeId) ?? null; const selectedEdge = edges.find((e) => e.id === selectedEdgeId) ?? null;
const [isPromptExpanded, setIsPromptExpanded] = useState(false); const [isPromptExpanded, setIsPromptExpanded] = useState(false);
const handleTogglePromptExpand = useCallback(() => { const handleTogglePromptExpand = useCallback(() => {
@@ -2210,6 +2215,7 @@ function InnerEditor({
<div <div
className={`wf-editor-body${workflowListStageOpen ? " wf-editor-body--list-stage" : " wf-editor-body--editor-stage"}${ className={`wf-editor-body${workflowListStageOpen ? " wf-editor-body--list-stage" : " wf-editor-body--editor-stage"}${
simpleLayoutEnabled ? " wf-editor-body--simple-layout" : "" simpleLayoutEnabled ? " wf-editor-body--simple-layout" : ""
}${mobileNodeDetailStage ? " wf-editor-body--mobile-node-detail" : ""
}`} }`}
> >
<aside className="wf-editor-sidebar"> <aside className="wf-editor-sidebar">
@@ -2508,6 +2514,7 @@ function InnerEditor({
onSelectNode={(id) => { onSelectNode={(id) => {
setSelectedNodeId(id); setSelectedNodeId(id);
setSelectedEdgeId(null); setSelectedEdgeId(null);
setInspectorCollapsed(false);
}} }}
onSelectEdge={(id) => { onSelectEdge={(id) => {
setSelectedEdgeId(id); setSelectedEdgeId(id);
@@ -3101,6 +3108,7 @@ function InnerEditor({
onNodeClick={(_, node) => { onNodeClick={(_, node) => {
setSelectedNodeId(node.id); setSelectedNodeId(node.id);
setSelectedEdgeId(null); setSelectedEdgeId(null);
setInspectorCollapsed(false);
}} }}
onEdgeClick={(_, edge) => { onEdgeClick={(_, edge) => {
setSelectedEdgeId(edge.id); setSelectedEdgeId(edge.id);
@@ -3143,9 +3151,7 @@ function InnerEditor({
)} )}
</section> </section>
{selectedNode && {selectedNodeHasInspector &&
selectedNode.data.kind !== "start" &&
selectedNode.data.kind !== "end" &&
!(isMobileViewport && inspectorCollapsed) && !(isMobileViewport && inspectorCollapsed) &&
!(compactLayoutEnabled && !isMobileViewport) && ( !(compactLayoutEnabled && !isMobileViewport) && (
<aside className="wf-editor-inspector" data-testid="wf-node-inspector"> <aside className="wf-editor-inspector" data-testid="wf-node-inspector">
@@ -3157,7 +3163,13 @@ function InnerEditor({
className="wf-inspector-toggle wf-inspector-toggle--expanded" className="wf-inspector-toggle wf-inspector-toggle--expanded"
data-testid="wf-inspector-toggle" data-testid="wf-inspector-toggle"
aria-expanded="true" aria-expanded="true"
onClick={() => setInspectorCollapsed(true)} onClick={() => {
if (simpleLayoutEnabled) {
setSelectedNodeId(null);
} else {
setInspectorCollapsed(true);
}
}}
> >
<ChevronDown size={13} /> <ChevronDown size={13} />
<span>{t("workflowNodes.collapseInspector", "Collapse")}</span> <span>{t("workflowNodes.collapseInspector", "Collapse")}</span>

View File

@@ -100,6 +100,15 @@ describe("WorkflowNodeEditor mobile CSS contract", () => {
expect(inspectorRule).toMatch(/flex\s*:\s*1 1 auto\s*;/); expect(inspectorRule).toMatch(/flex\s*:\s*1 1 auto\s*;/);
expect(inspectorRule).toMatch(/min-height\s*:\s*0\s*;/); expect(inspectorRule).toMatch(/min-height\s*:\s*0\s*;/);
const mobileDetailCanvasRule = findRule(mobileBlocks, /\.wf-editor-body--mobile-node-detail \.wf-editor-canvas-wrap\s*\{[^}]*\}/);
expect(mobileDetailCanvasRule).toMatch(/display\s*:\s*none\s*;/);
const mobileDetailInspectorRule = findRule(mobileBlocks, /\.wf-editor-body--mobile-node-detail \.wf-editor-inspector\s*\{[^}]*\}/);
expect(mobileDetailInspectorRule).toMatch(/display\s*:\s*flex\s*;/);
expect(mobileDetailInspectorRule).toMatch(/flex\s*:\s*1 1 auto\s*;/);
expect(mobileDetailInspectorRule).toMatch(/min-height\s*:\s*0\s*;/);
expect(mobileDetailInspectorRule).toMatch(/max-height\s*:\s*none\s*;/);
const collapsedToggleRule = findRule([editorCss], /\.wf-inspector-toggle--collapsed\s*\{[^}]*\}/); const collapsedToggleRule = findRule([editorCss], /\.wf-inspector-toggle--collapsed\s*\{[^}]*\}/);
expect(collapsedToggleRule).toMatch(/position\s*:\s*absolute\s*;/); expect(collapsedToggleRule).toMatch(/position\s*:\s*absolute\s*;/);
expect(collapsedToggleRule).toMatch(/bottom\s*:\s*var\(--space-sm\)\s*;/); expect(collapsedToggleRule).toMatch(/bottom\s*:\s*var\(--space-sm\)\s*;/);

View File

@@ -499,18 +499,20 @@ describe("WorkflowNodeEditor", () => {
render(<WorkflowNodeEditor isOpen onClose={() => {}} addToast={() => {}} />); render(<WorkflowNodeEditor isOpen onClose={() => {}} addToast={() => {}} />);
fireEvent.click(await screen.findByRole("button", { name: "QA" })); fireEvent.click(await screen.findByRole("button", { name: "QA" }));
fireEvent.click(await screen.findByTestId("wf-node-gate")); const mobileGateRow = await screen.findByTestId("mobile-wf-node-lint");
fireEvent.click(within(mobileGateRow).getByRole("button"));
const inspector = await screen.findByTestId("wf-node-inspector"); const inspector = await screen.findByTestId("wf-node-inspector");
expect(within(inspector).getByLabelText("Prompt")).toBeInTheDocument(); expect(within(inspector).getByLabelText("Prompt")).toBeInTheDocument();
expect(inspector.closest(".wf-editor-body")).toHaveClass("wf-editor-body--mobile-node-detail");
fireEvent.click(screen.getByTestId("wf-inspector-toggle")); fireEvent.click(screen.getByTestId("wf-inspector-toggle"));
await waitFor(() => expect(screen.queryByTestId("wf-node-inspector")).not.toBeInTheDocument()); await waitFor(() => expect(screen.queryByTestId("wf-node-inspector")).not.toBeInTheDocument());
expect(screen.queryByLabelText("Prompt")).not.toBeInTheDocument(); expect(screen.queryByLabelText("Prompt")).not.toBeInTheDocument();
expect(screen.getByTestId("wf-inspector-toggle")).toHaveAttribute("aria-expanded", "false"); expect(await screen.findByTestId("mobile-wf-graph")).toBeVisible();
fireEvent.click(screen.getByTestId("wf-inspector-toggle")); fireEvent.click(within(await screen.findByTestId("mobile-wf-node-lint")).getByRole("button"));
expect(await screen.findByTestId("wf-node-inspector")).toBeInTheDocument(); expect(await screen.findByTestId("wf-node-inspector")).toBeInTheDocument();
expect(screen.getByTestId("wf-inspector-toggle")).toHaveAttribute("aria-expanded", "true"); expect(screen.getByTestId("wf-inspector-toggle")).toHaveAttribute("aria-expanded", "true");
@@ -523,13 +525,13 @@ describe("WorkflowNodeEditor", () => {
render(<WorkflowNodeEditor isOpen onClose={() => {}} addToast={() => {}} />); render(<WorkflowNodeEditor isOpen onClose={() => {}} addToast={() => {}} />);
fireEvent.click(await screen.findByRole("button", { name: "QA" })); fireEvent.click(await screen.findByRole("button", { name: "QA" }));
fireEvent.click(await screen.findByTestId("wf-node-gate")); fireEvent.click(within(await screen.findByTestId("mobile-wf-node-lint")).getByRole("button"));
expect(await screen.findByTestId("wf-node-inspector")).toBeInTheDocument(); expect(await screen.findByTestId("wf-node-inspector")).toBeInTheDocument();
fireEvent.click(screen.getByTestId("wf-inspector-toggle")); fireEvent.click(screen.getByTestId("wf-inspector-toggle"));
await waitFor(() => expect(screen.queryByTestId("wf-node-inspector")).not.toBeInTheDocument()); await waitFor(() => expect(screen.queryByTestId("wf-node-inspector")).not.toBeInTheDocument());
fireEvent.click(await screen.findByTestId("wf-node-merge")); fireEvent.click(within(await screen.findByTestId("mobile-wf-node-merge")).getByRole("button"));
const inspector = await screen.findByTestId("wf-node-inspector"); const inspector = await screen.findByTestId("wf-node-inspector");
expect(within(inspector).getByLabelText("Name")).toBeInTheDocument(); expect(within(inspector).getByLabelText("Name")).toBeInTheDocument();