Skip to content

[DREAM-755] Draggable autocompleter on Pragmatic DnD (new sortable-lists engine + directives)#24229

Open
myabc wants to merge 5 commits into
devfrom
code-maintenance/77285-draggable-autocomplete-pragmatic-dnd
Open

[DREAM-755] Draggable autocompleter on Pragmatic DnD (new sortable-lists engine + directives)#24229
myabc wants to merge 5 commits into
devfrom
code-maintenance/77285-draggable-autocomplete-pragmatic-dnd

Conversation

@myabc

@myabc myabc commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

Ticket

https://community.openproject.org/wp/DREAM-755
https://community.openproject.org/wp/DREAM-756 (engine commit; the consumer migrations stack on this PR in #24243)

What are you trying to accomplish?

Migrates the Angular draggable autocompleter — the chip list used for column selection in the work package table configuration modal, and its other embeddings — from Dragula to Pragmatic drag and drop, and introduces the foundation the remaining Angular DnD surfaces migrate onto: a framework-neutral sortable-lists engine plus thin Angular directives. This is one step in retiring Dragula, not the last one: ng2-dragula stays in the tree for now, since the team planner and the shared DragAndDropService still depend on it.

Two bugs surfaced while migrating and are fixed here:

  • Two draggable autocompleters on the same page reacted to each other's drags, because the drag payload was keyed by a module-level Symbol shared across every instance. The payload scope is now created per root instance.
  • Dropping a chip outside the list still reordered it next to the last chip hovered, because item drop targets were unconditionally sticky. Stickiness is now bounded to the items' geometric span, so a drop outside cancels instead.

The remove affordance is now a real <button> with an aria-label, replacing a <div> with tabindex and hand-rolled enter/space key handlers.

Drag-and-drop screens touched

The draggable autocompleter is the only user-facing surface this PR changes. Its embeddings (breadcrumbs per the drag-and-drop inventory in https://community.openproject.org/wp/DREAM-777):

  • Project › Work packages › Configure view › Columns (modal tab)
  • Project › Work packages › Export dialog — column selection, including the PDF report export settings
  • Project lists › Configure view (modal)
  • Administration › Users and permissions › Users › Configure view (modal)
  • Administration › Work packages — default columns for work package lists
  • Administration › Projects — default columns for project lists
  • Resource planner › Configure view — column selection (Enterprise resource management module)

The engine and directives ship no other consumer in this PR; the remaining Angular surfaces (work package table, card view, boards, BIM BCF) follow in #24243.

Screenshots

Not yet captured — manual browser verification is still outstanding (see the checklist below), so I would rather not publish stills that imply it is done.

For review, the visible change is the drag affordance itself: Dragula's mirror element and placeholder gap are replaced by a 2px accent line (--fgColor-accent) on the edge where the chip would be inserted, and the dragged chip dims to 0.4 opacity while in flight.

What approach did you choose and why?

Three layers, one commit each:

  1. A framework-neutral engine (core-common/drag-and-drop/sortable-lists-engine.ts). createSortableRoot owns the Pragmatic wiring for one drag scope: item/list drop-target registration, a single root monitor that resolves every drop from the target stack into an intent (the pattern both the Stimulus root controller and Atlassian's React examples use), an optimistic two-phase transaction (complete from the target side, finalize from the source side; busy state serializes drags until both settle), axis-aware bounded stickiness with item→list handoff, drag-start suppression on interactive descendants, and a custom-preview hook. The engine also owns the DOM state attributes, deliberately the same contract the Stimulus sortable-lists controllers established — data-dragging="source", data-drop-position (+ owner), data-drop-container, data-sortable-lists-busy — so the two frontends share one styling vocabulary.
  2. Directives as a thin binding. opSortableLists (root) / opSortableListsList / opSortableListsItem, mirroring the Stimulus root/list/item topology. A root without explicit child lists collapses to a single implicit list, so a simple consumer like the autocompleter binds one attribute and one (opSortableListsDrop) handler. Registration follows the Angular view lifecycle (@for churn is free); cross-list moves emit removed on the source list before drop on the target list, carrying the transaction — that contract is what [DREAM-756] WP table, cards, boards and BCF on Pragmatic DnD #24243's boards migration consumes.
  3. The autocompleter on that API, completing its drop transactions synchronously since chips persist on form submit.

An earlier iteration wired the Pragmatic adapters directly in the component; a second one put all behavior in the directives. Both were discarded: the first had to re-pair rendered chips with items after every view change, the second would have duplicated the engine per framework once the DragAndDropService rewrite (#24243, stacked on this branch) needed the same core for the work-package fast-table, whose builder-rendered rows can't host directives.

dragAreaName is kept as a deprecated input. Instance isolation no longer needs a group name, but custom-element embeddings still pass the attribute, so removing it now would break them silently.

Two changes worth a conscious decision from a reviewer, since neither is strictly about this component:

  • test.isolate: true (its own commit). Specs that drive the real Pragmatic adapters and the Stimulus sortable-lists specs that mock the same @atlaskit module ids share one module registry, because the Angular builder defaults isolate to false to mimic Karma. Whichever file imports first wins the cache and the other sees a real function where it expects a spy — which shows up as a flaky, import-order-dependent failure count rather than a clean one. Isolating fixes it, and costs roughly 22s → 99s of frontend suite wall clock.
  • column_spec.rb moved from firefox_de to chrome_de. Reordering columns is a native HTML5 drag now, and geckodriver starts one — dragstart and dragover both fire — but never completes it, so no drop ever reaches the page and the columns silently stay put. This is a limitation of the driver, not of the feature; real Firefox users drag fine. The German locale the example has always carried is preserved.

Merge checklist

  • Added/updated tests
  • Added/updated documentation in Lookbook (patterns, previews, etc) — n/a, this is an Angular component with no Lookbook presence
  • Tested major browsers (Chrome, Firefox, Edge, ...) — outstanding: reorder, drop-outside-cancels, chip removal, append beyond the last chip, and two coexisting autocompleters on one page still need a manual pass

@myabc
myabc changed the base branch from dev to implementation/74970-backlogs-pragmatic-dnd July 13, 2026 10:24
@myabc myabc changed the title Code maintenance/77285 draggable autocomplete pragmatic dnd [DREAM-755] Migrate Angular draggable-autocompleter from Dragula to Pragmatic DnD Jul 13, 2026
@myabc
myabc requested a review from Copilot July 13, 2026 10:26

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 migrates the Angular DraggableAutocompleteComponent from Dragula (ng2-dragula) to Atlaskit Pragmatic Drag and Drop, aligning this autocompleter’s chip reordering behavior with the newer DnD approach already used elsewhere in the frontend.

Changes:

  • Remove Dragula module usage from shared/autocompleter Angular modules and replace chip reordering with Pragmatic DnD (draggable, dropTargetForElements, monitorForElements).
  • Introduce reusable Pragmatic DnD helper utilities (payload tagging, reorder helper, drop indicator, auto-scroll).
  • Add a dedicated component spec suite and update chip styling to reflect drag/drop state (opacity + edge indicator).

Reviewed changes

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

Show a summary per file
File Description
frontend/src/app/shared/shared.module.ts Removes DragulaModule from shared module imports.
frontend/src/app/shared/helpers/drag-and-drop/pragmatic/reorder.ts Adds reorderById helper + closest-edge re-export utilities for list reordering.
frontend/src/app/shared/helpers/drag-and-drop/pragmatic/payload.ts Adds symbol-keyed payload helpers to identify sortable items during drags.
frontend/src/app/shared/helpers/drag-and-drop/pragmatic/drop-indicator.ts Adds attribute-based drop indicator contract (data-drop-position).
frontend/src/app/shared/helpers/drag-and-drop/pragmatic/auto-scroll.ts Adds helper to register Pragmatic auto-scroll for sortable drags.
frontend/src/app/shared/components/autocompleter/openproject-autocompleter.module.ts Removes DragulaModule from autocompleter module imports.
frontend/src/app/shared/components/autocompleter/draggable-autocomplete/draggable-autocomplete.component.ts Replaces Dragula wiring with Pragmatic DnD monitor + chip-level draggable/drop targets.
frontend/src/app/shared/components/autocompleter/draggable-autocomplete/draggable-autocomplete.component.spec.ts Adds unit tests for rendering, selection/removal, hidden inputs, and drag-reorder behavior.
frontend/src/app/shared/components/autocompleter/draggable-autocomplete/draggable-autocomplete.component.sass Adds drag opacity and left/right edge drop indicator styling.
frontend/src/app/shared/components/autocompleter/draggable-autocomplete/draggable-autocomplete.component.html Removes Dragula directives and adds #chip refs for @ViewChildren registration.

Base automatically changed from implementation/74970-backlogs-pragmatic-dnd to dev July 14, 2026 12:27
@myabc
myabc force-pushed the code-maintenance/77285-draggable-autocomplete-pragmatic-dnd branch 2 times, most recently from 8be7973 to bac06fc Compare July 15, 2026 14:41
@myabc
myabc requested a review from Copilot July 15, 2026 14:41

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 17 out of 17 changed files in this pull request and generated 2 comments.

@myabc
myabc force-pushed the code-maintenance/77285-draggable-autocomplete-pragmatic-dnd branch from bac06fc to 87709c2 Compare July 15, 2026 21:16
@myabc
myabc marked this pull request as ready for review July 15, 2026 21:18
@github-actions

Copy link
Copy Markdown

Warning

Flaky specs

  • rspec ./spec/features/projects/create_spec.rb[1:12:3:2:1]
🤖 Ask Copilot to investigate

Copy the prompt below into a new comment on this PR to delegate the investigation to GitHub Copilot. It will look into the flakiness and open a separate pull request with you as reviewer.

@copilot The following spec(s) are flaky in CI (first seen on PR #24229, linked for reference only):

- `rspec ./spec/features/projects/create_spec.rb[1:12:3:2:1]`

Treat this as a standalone task, unrelated to PR #24229. Create a new branch from origin/dev and open a new pull request targeting dev — do not stack it on PR #24229 or reuse that branch.

Follow the playbook in docs/development/testing/handling-flaky-tests/README.md to find the root cause and fix the underlying race — do not skip, delete, or weaken the spec to make it pass; disabling is a last resort per the playbook, and only with a bug ticket. Verify the fix by running the spec(s) repeatedly (e.g. `script/bulk_run_rspec --run-count 10`).

If you cannot reproduce the flake or are not confident in a fix after reasonable investigation, do not fabricate a change or skip the spec to force CI green. Instead, leave the pull request in draft and document what you tried, the suspected cause, and any leads in its description, then assign @myabc to take over.

Once the fix is verified, title the PR after the spec(s) it fixes, and use the PR description to explain the root cause, how the change resolves it, and the before/after results. Label the PR `flaky-spec`, assign @myabc, and request a review from @myabc.
On every commit, set @myabc as the sole co-author with a `Co-authored-by:` trailer (use their GitHub no-reply email so it links to their account), so it is traceable who dispatched the fix.

@myabc myabc added this to the 17.8.x milestone Jul 16, 2026
@myabc myabc added javascript Pull requests that update Javascript code maintenance DO NOT MERGE and removed needs review labels Jul 16, 2026
@myabc myabc changed the title [DREAM-755] Migrate Angular draggable-autocompleter from Dragula to Pragmatic DnD [DREAM-755] Introduce sortable-lists engine and Angular directives on Pragmatic DnD Jul 16, 2026
myabc added 5 commits July 17, 2026 19:14
Replaces the component's direct Dragula usage with Pragmatic drag and
drop, integrated through new opSortableLists/opSortableListsItem
directives so Angular's view lifecycle owns each adapter registration
instead of re-pairing rendered chips with items after view changes.

The drag payload is scoped per list instance and drop-target
stickiness is bounded to the list rectangle, so coexisting
autocompleters cannot react to each other's drags and a drop outside
the list cancels rather than reordering next to the last hovered chip.

Framework-neutral helpers move to `src/common/drag-and-drop/` under a
new `core-common` alias, reusable by the Stimulus sortable-lists
controllers and the upcoming `DragAndDropService` rewrite.

https://community.openproject.org/wp/DREAM-755
The builder defaults `isolate` to false to mimic Karma, sharing one
module registry across specs. Specs driving the real Pragmatic adapters
and specs mocking the same `@atlaskit` ids poison each other, whichever
imports first. Costs ~75s of suite wall clock.
Adds a framework-neutral Pragmatic engine to core-common: a root
monitor resolves drops into intents, an optimistic two-phase
transaction serializes drags via a busy attribute, and the DOM
attribute contract matches the Stimulus sortable-lists controllers.
Also extends the shared helpers: list-aware payloads, append
reorders and drop-indicator ownership.

https://community.openproject.org/wp/DREAM-756
Rebinds the directives as a thin layer over the shared engine in
the final three-role shape: root, list and item. A root without
explicit lists collapses to one implicit list; cross-list moves
emit removed before drop, carrying the transaction for optimistic
completion.

https://community.openproject.org/wp/DREAM-755
Completes drop transactions synchronously, since chips only persist
on form submit, and covers the append, remove-button suppression
and consecutive-drag paths.

https://community.openproject.org/wp/DREAM-755
@myabc
myabc force-pushed the code-maintenance/77285-draggable-autocomplete-pragmatic-dnd branch from b218533 to 9fb3902 Compare July 18, 2026 17:46
@github-actions

github-actions Bot commented Jul 22, 2026

Copy link
Copy Markdown

Deploying openproject with PullPreview

Field Value
Latest commit 9fb3902
Job deploy
Status ✅ Deploy successful
Preview URL https://pr-24229-77285-draggable-a-ip-128-140-89-31.my.opf.run:443

View logs

@myabc myabc changed the title [DREAM-755] Introduce sortable-lists engine and Angular directives on Pragmatic DnD [DREAM-755] Draggable autocompleter on Pragmatic DnD (new sortable-lists engine + directives) Jul 23, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

javascript Pull requests that update Javascript code maintenance needs review pullpreview

Development

Successfully merging this pull request may close these issues.

2 participants