FN-6297: make mobile edge details full-screen
Makes workflow edge details behave like a full-screen mobile editor stage while keeping the mobile tabs visible. - Add a mobile edge-detail editor body state that hides the canvas and expands the inspector.\n- Add a dismiss control to the mobile edge inspector.\n- Preserve the top mobile tab row sizing and cover the edge-detail/mobile-tab CSS contracts with tests. Files changed:\n .../app/components/WorkflowNodeEditor.css | 19 +++++++++++++\n .../app/components/WorkflowNodeEditor.tsx | 22 ++++++++++++---\n .../__tests__/WorkflowNodeEditor.css.test.ts | 11 ++++++++\n .../__tests__/WorkflowNodeEditor.test.tsx | 31 ++++++++++++++++++++++\n 4 files changed, 80 insertions(+), 3 deletions(-) Fusion-Task-Id: FN-6297 Fusion-Task-Lineage: be7cea97-0790-4d88-9fa5-3d484b4d3dfb
This commit is contained in:
@@ -1470,9 +1470,11 @@
|
||||
|
||||
.wf-mobile-tabs {
|
||||
display: flex;
|
||||
flex: 0 0 auto;
|
||||
gap: var(--space-xs);
|
||||
padding: var(--space-sm);
|
||||
overflow-x: auto;
|
||||
overflow-y: visible;
|
||||
border-bottom: 1px solid var(--border);
|
||||
}
|
||||
|
||||
@@ -1722,4 +1724,21 @@
|
||||
border-top: none;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.wf-editor-body--mobile-edge-detail .wf-editor-canvas-wrap {
|
||||
display: none;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
.wf-editor-body--mobile-edge-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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1878,8 +1878,9 @@ function InnerEditor({
|
||||
selectedNode !== null &&
|
||||
selectedNode.data.kind !== "start" &&
|
||||
selectedNode.data.kind !== "end";
|
||||
const mobileNodeDetailStage = isMobileMode && selectedNodeHasInspector && !inspectorCollapsed;
|
||||
const selectedEdge = edges.find((e) => e.id === selectedEdgeId) ?? null;
|
||||
const mobileNodeDetailStage = isMobileMode && selectedNodeHasInspector && !inspectorCollapsed;
|
||||
const mobileEdgeDetailStage = isMobileMode && selectedEdge !== null;
|
||||
const [isPromptExpanded, setIsPromptExpanded] = useState(false);
|
||||
const handleTogglePromptExpand = useCallback(() => {
|
||||
setIsPromptExpanded((prev) => !prev);
|
||||
@@ -2210,7 +2211,8 @@ function InnerEditor({
|
||||
<div
|
||||
className={`wf-editor-body${workflowListStageOpen ? " wf-editor-body--list-stage" : " wf-editor-body--editor-stage"}${
|
||||
simpleLayoutEnabled ? " wf-editor-body--simple-layout" : ""
|
||||
}${mobileNodeDetailStage ? " wf-editor-body--mobile-node-detail" : ""
|
||||
}${mobileNodeDetailStage ? " wf-editor-body--mobile-node-detail" : ""}${
|
||||
mobileEdgeDetailStage ? " wf-editor-body--mobile-edge-detail" : ""
|
||||
}`}
|
||||
>
|
||||
<aside className="wf-editor-sidebar">
|
||||
@@ -4118,7 +4120,21 @@ function InnerEditor({
|
||||
|
||||
{selectedEdge && (
|
||||
<aside className="wf-editor-inspector" data-testid="wf-edge-inspector">
|
||||
<h3>{t("workflowNodes.edgeInspector", "Edge")}</h3>
|
||||
<div className="wf-inspector-heading">
|
||||
<h3>{t("workflowNodes.edgeInspector", "Edge")}</h3>
|
||||
{isMobileMode && (
|
||||
<button
|
||||
type="button"
|
||||
className="wf-inspector-toggle wf-inspector-toggle--expanded"
|
||||
data-testid="wf-edge-inspector-close"
|
||||
aria-expanded="true"
|
||||
onClick={() => setSelectedEdgeId(null)}
|
||||
>
|
||||
<ChevronDown size={13} />
|
||||
<span>{t("workflowNodes.collapseInspector", "Collapse")}</span>
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
<fieldset className="wf-inspector-fields" disabled={isBuiltin}>
|
||||
{selectedEdgeEditability === "verdicts" ? (
|
||||
<>
|
||||
|
||||
@@ -109,6 +109,17 @@ describe("WorkflowNodeEditor mobile CSS contract", () => {
|
||||
expect(mobileDetailInspectorRule).toMatch(/min-height\s*:\s*0\s*;/);
|
||||
expect(mobileDetailInspectorRule).toMatch(/max-height\s*:\s*none\s*;/);
|
||||
|
||||
const mobileEdgeDetailCanvasRule = findRule(mobileBlocks, /\.wf-editor-body--mobile-edge-detail \.wf-editor-canvas-wrap\s*\{[^}]*\}/);
|
||||
expect(mobileEdgeDetailCanvasRule).toMatch(/display\s*:\s*none\s*;/);
|
||||
|
||||
const mobileEdgeDetailInspectorRule = findRule(mobileBlocks, /\.wf-editor-body--mobile-edge-detail \.wf-editor-inspector\s*\{[^}]*\}/);
|
||||
expect(mobileEdgeDetailInspectorRule).toMatch(/display\s*:\s*flex\s*;/);
|
||||
expect(mobileEdgeDetailInspectorRule).toMatch(/flex\s*:\s*1 1 auto\s*;/);
|
||||
expect(mobileEdgeDetailInspectorRule).toMatch(/max-height\s*:\s*none\s*;/);
|
||||
|
||||
const mobileTabsRule = findRule(mobileBlocks, /\.wf-mobile-tabs\s*\{[^}]*\}/);
|
||||
expect(mobileTabsRule).toMatch(/flex\s*:\s*0 0 auto\s*;/);
|
||||
|
||||
const collapsedToggleRule = findRule([editorCss], /\.wf-inspector-toggle--collapsed\s*\{[^}]*\}/);
|
||||
expect(collapsedToggleRule).toMatch(/position\s*:\s*absolute\s*;/);
|
||||
expect(collapsedToggleRule).toMatch(/bottom\s*:\s*var\(--space-sm\)\s*;/);
|
||||
|
||||
@@ -518,6 +518,37 @@ describe("WorkflowNodeEditor", () => {
|
||||
expect(screen.getByTestId("wf-inspector-toggle")).toHaveAttribute("aria-expanded", "true");
|
||||
});
|
||||
|
||||
it("opens selected edge details as a dismissible full-screen mobile stage", async () => {
|
||||
mockWorkflowEditorViewport("mobile");
|
||||
vi.mocked(fetchWorkflows).mockResolvedValue([v2Def()]);
|
||||
|
||||
render(<WorkflowNodeEditor isOpen onClose={() => {}} addToast={() => {}} />);
|
||||
|
||||
fireEvent.click(await screen.findByRole("button", { name: "Custom" }));
|
||||
await screen.findByText("Save");
|
||||
await screen.findByTestId("mobile-wf-graph");
|
||||
|
||||
const mobileEdgeChip = await screen.findByTestId("mobile-wf-edge-e-step-end-1");
|
||||
fireEvent.click(mobileEdgeChip);
|
||||
|
||||
const edgeInspector = await screen.findByTestId("wf-edge-inspector");
|
||||
const editorBody = edgeInspector.closest(".wf-editor-body");
|
||||
expect(editorBody).toHaveClass("wf-editor-body--mobile-edge-detail");
|
||||
expect(editorBody).not.toHaveClass("wf-editor-body--mobile-node-detail");
|
||||
expect(within(edgeInspector).getByRole("button", { name: /delete edge/i })).toBeInTheDocument();
|
||||
expect(screen.getByTestId("wf-mobile-shell").closest(".wf-editor-canvas-wrap")).toBeInTheDocument();
|
||||
|
||||
fireEvent.click(screen.getByTestId("wf-edge-inspector-close"));
|
||||
await waitFor(() => expect(screen.queryByTestId("wf-edge-inspector")).not.toBeInTheDocument());
|
||||
expect(await screen.findByTestId("mobile-wf-graph")).toBeVisible();
|
||||
|
||||
fireEvent.click(within(await screen.findByTestId("mobile-wf-node-step")).getByRole("button"));
|
||||
|
||||
const nodeInspector = await screen.findByTestId("wf-node-inspector");
|
||||
expect(nodeInspector.closest(".wf-editor-body")).toHaveClass("wf-editor-body--mobile-node-detail");
|
||||
expect(nodeInspector.closest(".wf-editor-body")).not.toHaveClass("wf-editor-body--mobile-edge-detail");
|
||||
});
|
||||
|
||||
it("auto-expands the mobile inspector when selecting another node", async () => {
|
||||
mockWorkflowEditorViewport("mobile");
|
||||
vi.mocked(fetchWorkflows).mockResolvedValue([def()]);
|
||||
|
||||
Reference in New Issue
Block a user