Skip to content

drag and drop with horizontal movement for level#47824

Open
edewit wants to merge 16 commits into
keycloak:mainfrom
edewit:drop-into-subflow
Open

drag and drop with horizontal movement for level#47824
edewit wants to merge 16 commits into
keycloak:mainfrom
edewit:drop-into-subflow

Conversation

@edewit

@edewit edewit commented Apr 7, 2026

Copy link
Copy Markdown
Contributor

This adds the ability to drag and drop a excution into a subflow

related: #44026
Signed-off-by: Erik Jan de Wit erikjan.dewit@gmail.com

@edewit
edewit requested review from a team as code owners April 7, 2026 14:52
@edewit
edewit force-pushed the drop-into-subflow branch from 6766f41 to 3bb5fde Compare April 14, 2026 14:26
@edewit

edewit commented May 18, 2026

Copy link
Copy Markdown
Contributor Author

@ssilvert could you review?

@edewit
edewit force-pushed the drop-into-subflow branch from cbb8f2b to 0013427 Compare May 18, 2026 08:39
@ssilvert

Copy link
Copy Markdown
Contributor

@edewit I'm getting compilation errors when I try to run vite.

@edewit

edewit commented May 20, 2026

Copy link
Copy Markdown
Contributor Author

ahh yeah sorry... there was a merge error

@edewit

edewit commented May 22, 2026

Copy link
Copy Markdown
Contributor Author
image so now we use horizontal movement to determine the level of where to drop

@edewit edewit changed the title drag and drop with shift to drop into subflow drag and drop with horizontal movement for level May 22, 2026
@edewit
edewit force-pushed the drop-into-subflow branch from d6af886 to 63939c6 Compare May 22, 2026 06:34
@ssilvert
ssilvert force-pushed the drop-into-subflow branch from 1e33a7a to ecc3071 Compare July 7, 2026 18:54
Copilot AI review requested due to automatic review settings July 7, 2026 18:54

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR replaces the Admin UI authentication flow reordering implementation with a new drag-and-drop approach (dnd-kit) that supports horizontal movement to change nesting level, enabling dropping an execution into a subflow.

Changes:

  • Migrates flow table drag/drop from PatternFly’s DragDrop to @dnd-kit/core with a dedicated grip handle and drag overlay.
  • Adds “horizontal indent” handling during drag to preview/perform nesting into subflows, backed by new ExecutionList.resolveDropTarget() logic and unit tests.
  • Updates Playwright E2E coverage and adds new UI strings + styling for drag affordances and drop indicators.

Reviewed changes

Copilot reviewed 9 out of 10 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
js/pnpm-lock.yaml Locks newly added dnd-kit dependencies.
js/apps/admin-ui/package.json Adds dnd-kit dependencies for the Admin UI.
js/apps/admin-ui/src/authentication/FlowDetails.tsx Introduces dnd-kit context, drag sensors, live announcements, and overlay; wires drop preview + execution changes.
js/apps/admin-ui/src/authentication/execution-model.ts Adds drop targeting/preview types and implements drop resolution for reorder vs level-change.
js/apps/admin-ui/src/authentication/components/FlowRow.tsx Adds a per-row grip drag handle, drop preview state rendering, and related data attributes.
js/apps/admin-ui/src/authentication/components/flow-row.css Adds visual styling for drag state, drop lines, nesting highlight, and overlay.
js/apps/admin-ui/src/authentication/components/FlowTitle.tsx Removes the unused “step” type/icon usage to align with the updated row typing.
js/apps/admin-ui/src/authentication/tests/ExecutionList.test.ts Adds unit tests for getChange and resolveDropTarget behavior across nested/collapsed cases.
js/apps/admin-ui/test/autentication/flows.spec.ts Updates existing DnD test to use the grip handle + adds a new horizontal-indent nesting test.
js/apps/admin-ui/maven-resources/theme/keycloak.v2/admin/messages/messages_en.properties Adds new live-region/message strings for drag instructions and “move into subflow” announcements.
Files not reviewed (1)
  • js/pnpm-lock.yaml: Generated file

Comment on lines +255 to +269
const targetParent =
vertical === "into"
? hover
: clampedLevel > 0
? this.#getParentFromVisualOrder(
clampedLevel,
insertIndex,
visualOrder,
)
: undefined;

const targetParentId =
clampedLevel > 0 && targetParent?.authenticationFlow
? (targetParent.id ?? null)
: null;
Comment on lines +755 to +767
{activeId ? (
<DragOverlayContent
execution={
executionList.order().find((ex) => ex.id === activeId)!
}
targetParentName={
dropInfo.targetParentId
? findExecutionById(dropInfo.targetParentId)
?.displayName
: undefined
}
/>
) : null}
Comment on lines +722 to +724
<Table aria-label={t("flows")} isTreeTable>
<FlowHeader />
<>
Comment on lines +944 to +945
onDragMoveIntoSubflow={{item}} is moved into {{subflow}}
executionDragInstructions=Drag and drop executions to reorder them. Moving them horizontally will change on which level they are inserted.
Comment on lines +87 to +95
.keycloak__authentication__drag-handle:hover {
color: var(--pf-v5-global--Color--100);
background-color: var(--pf-v5-global--BackgroundColor--200);
}

.keycloak__authentication__drag-handle:active {
cursor: grabbing;
}

Comment on lines 89 to 95
"@dagrejs/dagre": "^1.1.8",
"@dnd-kit/core": "^6.1.0",
"@dnd-kit/sortable": "^8.0.0",
"@dnd-kit/utilities": "^3.2.2",
"@keycloak/keycloak-admin-client": "workspace:*",
"@keycloak/keycloak-ui-shared": "workspace:*",
"@patternfly/patternfly": "^5.4.2",
Copilot AI review requested due to automatic review settings July 13, 2026 08:45

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 9 out of 10 changed files in this pull request and generated 6 comments.

Files not reviewed (1)
  • js/pnpm-lock.yaml: Generated file

Comment on lines +90 to +92
"@dnd-kit/core": "^6.1.0",
"@dnd-kit/sortable": "^8.0.0",
"@dnd-kit/utilities": "^3.2.2",
Comment on lines +943 to +945
onDragMove=Dragging item {{item}}
onDragMoveIntoSubflow={{item}} is moved into {{subflow}}
executionDragInstructions=Drag and drop executions to reorder them. Moving them horizontally will change on which level they are inserted.
Comment on lines +489 to +491
const currentDropInfo = dropInfo;
setDropInfo(emptyDropInfo());

} from "@patternfly/react-icons";
import { Table, Tbody } from "@patternfly/react-table";
import { useState } from "react";
import { useCallback, useMemo, useRef, useState } from "react";
Comment on lines +409 to +412
const { active } = event;
const { x: pointerX, y: pointerY } = pointer;
const rows = document.querySelectorAll("tr[data-execution-id]");
const hoveredRow = findHoveredRow(
Comment on lines +245 to +249
const collapseSnapshotRef = useRef<Map<string, boolean>>(new Map());
const autoExpandedIdsRef = useRef<Set<string>>(new Set());
const expandTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
const pendingExpandIdRef = useRef<string | null>(null);
const collapseRafRef = useRef<number | null>(null);
Copilot AI review requested due to automatic review settings July 13, 2026 10:05

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 9 out of 10 changed files in this pull request and generated 6 comments.

Files not reviewed (1)
  • js/pnpm-lock.yaml: Generated file

Comment on lines +424 to +427
const { rect, isSubflow } = hoveredRow;
const rowHeight = rect.height;
const relativeY = pointerY - rect.top;
const edgeZone = rowHeight * ROW_EDGE_ZONE_RATIO;
Comment on lines +261 to +267
const sensors = useSensors(
useSensor(PointerSensor, {
activationConstraint: {
distance: 5,
},
}),
);
Comment on lines +338 to +342
? new LevelChange(
hover.executionList?.length || 0,
this.#siblingIndexAtEndOfSubflow(draggedId, hover),
hover,
)
Comment on lines +314 to +317
const needsLevelChange =
vertical === "into" ||
clampedLevel !== draggedLevel ||
currentParent?.id !== targetParent?.id;
Comment on lines +314 to +317
const needsLevelChange =
vertical === "into" ||
clampedLevel !== draggedLevel ||
currentParent?.id !== targetParent?.id;
},
});

await assertNotificationMessage(page, "Flow successfully updated");
Copilot AI review requested due to automatic review settings July 13, 2026 10:28

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 12 out of 13 changed files in this pull request and generated 2 comments.

Files not reviewed (1)
  • js/pnpm-lock.yaml: Generated file

Comment on lines +343 to +347
? new LevelChange(
hover.executionList?.length || 0,
this.#siblingIndexAtEndOfSubflow(draggedId, hover),
hover,
)
Comment on lines +279 to +285
const sensors = useSensors(
useSensor(PointerSensor, {
activationConstraint: {
distance: 5,
},
}),
);
Copilot AI review requested due to automatic review settings July 13, 2026 10:44

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 12 out of 13 changed files in this pull request and generated 4 comments.

Files not reviewed (1)
  • js/pnpm-lock.yaml: Generated file


return {
kind: "reorder",
change: this.getChange(dragged, order),
Comment on lines +314 to +317
const needsLevelChange =
vertical === "into" ||
clampedLevel !== draggedLevel ||
currentParent?.id !== targetParent?.id;
Comment on lines +279 to +285
const sensors = useSensors(
useSensor(PointerSensor, {
activationConstraint: {
distance: 5,
},
}),
);

it("returns null for into on non-subflow", () => {
const list = new ExecutionList(list2Data);
expect(list.resolveDropTarget("1", "1", "into")).toBeNull();
Copilot AI review requested due to automatic review settings July 14, 2026 09:42

@ssilvert ssilvert left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried this out and I think it's getting very close to what we want.

The flows now expand when you hover, but they also need to contract when you hover so that you can change your mind and easily place something outside the flow.

It would also be helpful if the blue line indicating the target drop location looked different from the line showing a selection. Maybe a different color and appearance. For instance, here it's a little hard to tell which solid blue line is the drop indicator.

Image

Lastly, it would be better if The text said exactly where the moved execution will land. In the case above, it would be great if it said, "Cookie will be moved into Copy of browser forms below Username Password Form"

Copilot AI review requested due to automatic review settings July 17, 2026 10:28

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 13 out of 14 changed files in this pull request and generated 5 comments.

Files not reviewed (1)
  • js/pnpm-lock.yaml: Generated file

Comment on lines +254 to +258
const dragged = this.#findInTree(draggedId);
const hover = visualOrder[hoverIndex];
if (!dragged || draggedId === hoverId) {
return null;
}
Comment on lines +631 to +634
(change instanceof LevelChange &&
change.oldIndex === change.newIndex &&
resolved.kind === "level-change" &&
currentParent?.id === change.parent?.id);
Comment on lines +747 to +759
if (executionFlow.executionList?.length) {
const parentForChildren = { ...executionFlow, id: result.id };
for (const [i, child] of executionFlow.executionList.entries()) {
await executeChange(
child,
{
parent: parentForChildren,
newIndex: i,
oldIndex: i,
},
{ silent: true, snapshot: subtreeSnapshot.children[i] },
);
}

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is a known limitation of the drag and drop support in it's current state

Comment on lines +282 to +288
const sensors = useSensors(
useSensor(PointerSensor, {
activationConstraint: {
distance: 5,
},
}),
);

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a larger issue and something that the PF implementation also lacked it's been tracked in this issue #50863

Comment thread js/apps/admin-ui/test/autentication/flow.ts
Copilot AI review requested due to automatic review settings July 17, 2026 11:54
@edewit
edewit force-pushed the drop-into-subflow branch from 73d55bd to 6ccf92f Compare July 17, 2026 11:54
@edewit
edewit requested a review from ssilvert July 17, 2026 11:55
@edewit

edewit commented Jul 17, 2026

Copy link
Copy Markdown
Contributor Author

@ssilvert right that makes sense updated with your comments

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 13 out of 14 changed files in this pull request and generated 4 comments.

Files not reviewed (1)
  • js/pnpm-lock.yaml: Generated file

Comment thread js/apps/admin-ui/src/authentication/FlowDetails.tsx
Comment thread js/apps/admin-ui/src/authentication/FlowDetails.tsx
Comment thread js/apps/admin-ui/src/authentication/components/FlowRow.tsx
Comment thread js/apps/admin-ui/test/autentication/flows.spec.ts
edewit added 16 commits July 27, 2026 08:45
This adds the ability to drag and drop a excution into a subflow by
holding <shift> when starting the drag.

related: keycloak#44026
Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com>
Using dnd-kit directly for more control, now you can drop executions
into subflows and there is a visual indicator that when you do

Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com>
Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com>
Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com>
Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com>
Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com>
Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com>
Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com>
Instead of using horizontal movement to indicate the level we now use
collapse and expand

Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com>
some small css fixes that fix some adding and removing space

Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com>
Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com>
Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com>
Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com>
Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com>
Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com>
* The flows now also collapse when choosing another flow
* Used green for drop indicator
* Changed the text in the drag handler to specify entire destination

Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com>
Copilot AI review requested due to automatic review settings July 27, 2026 06:45
@edewit
edewit force-pushed the drop-into-subflow branch from 6ccf92f to 559a90d Compare July 27, 2026 06:45

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 13 out of 14 changed files in this pull request and generated no new comments.

Files not reviewed (1)
  • js/pnpm-lock.yaml: Generated file
Comments suppressed due to low confidence (4)

js/apps/admin-ui/src/authentication/FlowDetails.tsx:634

  • Dropping a non-last child onto its current parent should append it, but this check treats every such drop as a no-op because LevelChange.oldIndex is the post-recreation append index, not the child's current index. Compare dragged.index with change.newIndex; otherwise only the last child should be considered unchanged.
      (change instanceof LevelChange &&
        change.oldIndex === change.newIndex &&
        resolved.kind === "level-change" &&
        currentParent?.id === change.parent?.id);

js/apps/admin-ui/src/authentication/FlowDetails.tsx:781

  • For a recreated subflow, addFlowToFlow returns the new flow ID, not the execution ID (see AuthenticationManagementResource.java:503,557-558). Consequently this requirement lookup never finds the subflow execution, and any subsequent priority calls also use the wrong ID; resolve the newly created execution by matching its flowId before restoring requirement or priority.
        if (originalRequirement) {
          await restoreExecutionRequirement(id, originalRequirement);

js/apps/admin-ui/src/authentication/FlowDetails.tsx:728

  • The snapshot now captures authenticationConfig for every node, but the subflow branch never recreates it; form-flow subflow executions can carry a config (AuthenticationManagementResource.java:712-713). Moving such a subflow therefore permanently drops its configuration; recreate this config after resolving the new subflow execution ID.
        const subtreeSnapshot =
          snapshot ??
          (await snapshotExecutionSubtree(ex as ExpandableExecution));
        const config = subtreeSnapshot.config;

js/apps/admin-ui/src/authentication/FlowDetails.tsx:735

  • This recreation branch still hard-codes type: "basic-flow", so dragging a form-flow subflow changes its flow type and allowed execution/requirement semantics. Snapshot the referenced flow's provider/type and pass that original type to addFlowToFlow instead.
        if ("authenticationFlow" in ex && ex.authenticationFlow) {

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants