Skip to content

[AGILE-364] Apply batch selection to contextual ActionMenus - #24781

Open
myabc wants to merge 9 commits into
implementation/AGILE-363-batch-position-actionsfrom
implementation/AGILE-364-batch-action-menus
Open

[AGILE-364] Apply batch selection to contextual ActionMenus#24781
myabc wants to merge 9 commits into
implementation/AGILE-363-batch-position-actionsfrom
implementation/AGILE-364-batch-action-menus

Conversation

@myabc

@myabc myabc commented Aug 16, 2026

Copy link
Copy Markdown
Contributor

Note

This PR is part of a stack. Please review and merge in this order: #24525#24778#24779#24780 → this PR.

Ticket

https://community.openproject.org/wp/AGILE-364

What are you trying to accomplish?

Make every Backlogs ActionMenu entry point settle and present the same action scope, and present either the invoker's own actions or the batch actions — never both at once, per UI/UX review.

The change:

  • applies one synchronous preserve/replace/singular policy before More, right-click, Context Menu, and Shift+F10 open the menu
  • renders the invoker-only actions and the batch-capable actions as two headingless Primer groups and toggles them by the settled scope: a 2+ selection sees only the batch actions, a one-card scope the full singular menu
  • names the menu for its batch scope through the invoker's tooltip ("Actions for n selected work packages"), which is also the menu's accessible name via Primer's aria-labelledby wiring; the name reverts when the menu closes
  • falls back to the singular menu when a batch has nothing to offer, so the popover never renders empty, and silently preserves selection for fixed cards
  • extracts the four-direction move items into a shared SortableLists::MoveMenuItems descriptor (DREAM-775), which Backlogs now renders through

Screenshots

Screenshot 2026-08-20 at 12 20 27

What approach did you choose and why?

The Backlogs item controller listens at the two established pre-open boundaries: contextual beforeOpen and the menu popover's capture-phase beforetoggle. Both settle the scope through the root's selectForAction; duplicate delivery is idempotent, and the late include-fragment refresh remains non-mutating and scoped to the card menu's own popover.

The group toggle is keyed to how many actions the menu presents, not how many are executable: with hideUnavailable off a consumer keeps its items on screen disabled, and the group holding them must not disappear. Before the singular group hides, focus parks on a presented batch item — Primer dismisses the menu the moment focus leaves it.

The batch name is a separate, complete, plural-aware string rather than the singular name plus a suffix, so other locales keep their own preposition and word order. The controller reads the key from a Stimulus value on the row and carries no Backlogs vocabulary; rewriting the tooltip's text moves the visible label and the accessible name together, avoiding a WCAG 2.5.3 divergence.

Merge checklist

  • Added/updated tests
  • Added/updated documentation in Lookbook (patterns, previews, etc)
  • Tested major browsers (Chrome, Firefox, Edge, ...) — chromium only; desktop Design QA should confirm the batch tooltip wording, and screen-reader QA the batch name and its reversion

@myabc myabc changed the title implementation/AGILE 364 batch action menus [AGILE-364] Apply batch selection to contextual ActionMenus Aug 16, 2026
@myabc
myabc requested a lite review from Copilot August 16, 2026 09:50

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 unifies Backlogs work package ActionMenu behavior across all entry points (More button, right-click, Context Menu key, Shift+F10) by settling the live action scope before menu presentation, and by splitting the menu into stable singular vs batch action groups while preserving the one-card experience.

Changes:

  • Add a shared “prepareActionMenu” pre-open path (contextual beforeOpen + popover beforetoggle) so every invocation settles selection/action scope consistently.
  • Render two stable ActionMenu groups (“This work package” vs “Selected work packages”) and let the item controller project visibility + pluralized headings based on the resolved scope.
  • Strengthen batch move locking to include ordered source/target lifecycle locks and serialize unanchored placements (append/top), with expanded concurrency coverage.

Reviewed changes

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

Show a summary per file
File Description
modules/backlogs/spec/support/pages/backlog.rb Adds higher-level helpers for opening/validating menus via all entry points and probing batch move payloads.
modules/backlogs/spec/services/backlogs/work_packages/batch_update_service_spec.rb Updates lock-order expectations to match new lifecycle/placement locking.
modules/backlogs/spec/services/backlogs/work_packages/batch_update_service_concurrency_spec.rb Adds concurrency scenarios covering inbox placement/top moves and sprint-finish races.
modules/backlogs/spec/features/work_packages/move_via_menu_spec.rb Switches to new batch-aware menu action helper for positional actions.
modules/backlogs/spec/features/work_packages/card_context_menu_spec.rb Uses unified menu-opening helper for right-click and Shift+F10.
modules/backlogs/spec/features/work_packages/batch_action_menu_spec.rb New feature spec ensuring consistent batch projection across all menu entry points.
modules/backlogs/spec/components/backlogs/work_package_card_menu_component_spec.rb Verifies stable ActionMenu groups/headings and fixed-work-package behavior.
modules/backlogs/config/locales/js-en.yml Adds pluralized JS translation for “selected work packages” heading.
modules/backlogs/config/locales/en.yml Adds “This work package” translation for the menu group heading.
modules/backlogs/app/services/backlogs/work_packages/batch_update_service.rb Extends batch move synchronization: ordered lifecycle locks + placement serialization + tighter cohort validation.
modules/backlogs/app/components/backlogs/work_package_card_menu_component.html.erb Renders two Primer ActionMenu groups with hidden headings and moves batch actions into the batch group.
frontend/src/stimulus/controllers/dynamic/sortable-lists/selection-orchestrator.ts Removes now-unused move-collapse hook as action scope settling moves to menu preparation.
frontend/src/stimulus/controllers/dynamic/sortable-lists/selection-orchestrator.spec.ts Adds coverage for fixed invoker behavior without disturbing selection anchors.
frontend/src/stimulus/controllers/dynamic/sortable-lists/scrollable.controller.spec.ts Updates root stub to include the new prepareActionMenu port.
frontend/src/stimulus/controllers/dynamic/sortable-lists/list.controller.spec.ts Updates root stub to include the new prepareActionMenu port.
frontend/src/stimulus/controllers/dynamic/sortable-lists/item.controller.ts Adds pre-open listeners, projects grouped menu visibility/headings, and updates availability projection to use prepared scope.
frontend/src/stimulus/controllers/dynamic/sortable-lists/item.controller.spec.ts Adds extensive tests for pre-open sequencing and grouped heading/group projection.
frontend/src/stimulus/controllers/dynamic/sortable-lists/drag-and-drop.ts Updates SortableListsRoot port documentation and adds prepareActionMenu to the interface.
frontend/src/stimulus/controllers/dynamic/sortable-lists.controller.ts Implements prepareActionMenu by delegating to selection settling (selectForAction).
frontend/src/stimulus/controllers/dynamic/sortable-lists.controller.spec.ts Adds tests for action menu invocation scope behavior and idempotent duplicate delivery.
frontend/src/stimulus/controllers/dynamic/contextual-action-menu.controller.spec.ts Adds tests ensuring beforeOpen dispatch order, cancelability, and cancellation behavior.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread modules/backlogs/spec/support/pages/backlog.rb Outdated
@myabc
myabc force-pushed the implementation/AGILE-364-batch-action-menus branch from 9062c50 to b01e410 Compare August 16, 2026 19:03
@myabc
myabc force-pushed the implementation/AGILE-364-batch-action-menus branch from b01e410 to 8c9c0ad Compare August 16, 2026 23:39
@myabc
myabc force-pushed the implementation/AGILE-364-batch-action-menus branch from 967002f to ac54117 Compare August 17, 2026 00:06
@github-actions

github-actions Bot commented Aug 17, 2026

Copy link
Copy Markdown

Deploying openproject with PullPreview

Field Value
Latest commit f8c47f1
Job deploy
Status 🗑️ Preview destroyed
Preview URL Destroyed

View logs

@myabc
myabc force-pushed the implementation/AGILE-364-batch-action-menus branch from 4ae962a to 249c17e Compare August 17, 2026 12:20
@myabc myabc added the feature label Aug 17, 2026
@github-actions

Copy link
Copy Markdown

Warning

Flaky specs

  • rspec ./modules/backlogs/spec/features/inbox_column_spec.rb[1:7:1]
  • rspec ./modules/team_planner/spec/features/query_handling_spec.rb[1:3:1:1]
  • rspec ./modules/wikis/spec/features/admin/internal_provider_spec.rb[1:1]
  • rspec ./spec/features/work_packages/table/switch_types_spec.rb[1:1:1]
  • rspec ./spec/features/workflows/edit_spec.rb[1:4:4]
🤖 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 #24781, linked for reference only):

- `rspec ./modules/backlogs/spec/features/inbox_column_spec.rb[1:7:1]`
- `rspec ./modules/team_planner/spec/features/query_handling_spec.rb[1:3:1:1]`
- `rspec ./modules/wikis/spec/features/admin/internal_provider_spec.rb[1:1]`
- `rspec ./spec/features/work_packages/table/switch_types_spec.rb[1:1:1]`
- `rspec ./spec/features/workflows/edit_spec.rb[1:4:4]`

Treat this as a standalone task, unrelated to PR #24781. Create a new branch from origin/dev and open a new pull request targeting dev — do not stack it on PR #24781 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 force-pushed the implementation/AGILE-364-batch-action-menus branch from 249c17e to 1f2c6d0 Compare August 18, 2026 13:41
@github-actions

Copy link
Copy Markdown

Warning

Flaky specs

  • rspec ./modules/team_planner/spec/features/query_handling_spec.rb[1:3:1:1]
  • rspec ./spec/features/work_packages/table/queries/parent_field_ranking_spec.rb[1:1]
  • rspec ./spec/features/work_packages/table/switch_types_spec.rb[1:1:1]
  • rspec ./spec/features/work_packages/table/switch_types_spec.rb[1:1:2]
🤖 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 #24781, linked for reference only):

- `rspec ./modules/team_planner/spec/features/query_handling_spec.rb[1:3:1:1]`
- `rspec ./spec/features/work_packages/table/queries/parent_field_ranking_spec.rb[1:1]`
- `rspec ./spec/features/work_packages/table/switch_types_spec.rb[1:1:1]`
- `rspec ./spec/features/work_packages/table/switch_types_spec.rb[1:1:2]`

Treat this as a standalone task, unrelated to PR #24781. Create a new branch from origin/dev and open a new pull request targeting dev — do not stack it on PR #24781 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 force-pushed the implementation/AGILE-364-batch-action-menus branch 4 times, most recently from 446b4a8 to 3b7852b Compare August 18, 2026 22:52
@myabc
myabc marked this pull request as ready for review August 18, 2026 22:57
@github-actions

Copy link
Copy Markdown

Warning

Flaky specs

  • rspec ./modules/backlogs/spec/features/work_packages/batch_destination_menu_spec.rb[1:3]
  • rspec ./modules/backlogs/spec/features/work_packages/batch_destination_menu_spec.rb[1:4]
🤖 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 #24781, linked for reference only):

- `rspec ./modules/backlogs/spec/features/work_packages/batch_destination_menu_spec.rb[1:3]`
- `rspec ./modules/backlogs/spec/features/work_packages/batch_destination_menu_spec.rb[1:4]`

Treat this as a standalone task, unrelated to PR #24781. Create a new branch from origin/dev and open a new pull request targeting dev — do not stack it on PR #24781 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 requested a review from bsatarnejad August 19, 2026 11:46
@myabc
myabc force-pushed the implementation/AGILE-364-batch-action-menus branch 2 times, most recently from 2576bba to e994e8c Compare August 20, 2026 12:26
@myabc
myabc force-pushed the implementation/AGILE-364-batch-action-menus branch from e994e8c to f8c47f1 Compare August 20, 2026 14:40
@myabc myabc added needs review javascript Pull requests that update Javascript code ruby Pull requests that update Ruby code styling labels Aug 20, 2026
@github-actions

Copy link
Copy Markdown

Warning

Flaky specs

  • rspec ./modules/backlogs/spec/features/inbox_column_spec.rb[1:7:1]
  • rspec ./modules/backlogs/spec/features/work_packages/batch_destination_menu_spec.rb[1:1]
  • rspec ./spec/features/work_packages/details/inplace_editor/version_editor_spec.rb[1:1:2]
  • rspec ./spec/features/work_packages/table/queries/parent_field_ranking_spec.rb[1:1]
  • rspec ./spec/features/work_packages/table/switch_types_spec.rb[1:1:1]
  • rspec ./spec/features/work_packages/table/switch_types_spec.rb[1:1:2]
🤖 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 #24781, linked for reference only):

- `rspec ./modules/backlogs/spec/features/inbox_column_spec.rb[1:7:1]`
- `rspec ./modules/backlogs/spec/features/work_packages/batch_destination_menu_spec.rb[1:1]`
- `rspec ./spec/features/work_packages/details/inplace_editor/version_editor_spec.rb[1:1:2]`
- `rspec ./spec/features/work_packages/table/queries/parent_field_ranking_spec.rb[1:1]`
- `rspec ./spec/features/work_packages/table/switch_types_spec.rb[1:1:1]`
- `rspec ./spec/features/work_packages/table/switch_types_spec.rb[1:1:2]`

Treat this as a standalone task, unrelated to PR #24781. Create a new branch from origin/dev and open a new pull request targeting dev — do not stack it on PR #24781 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.

@github-actions

Copy link
Copy Markdown

Warning

Flaky specs

  • rspec ./spec/features/work_packages/table/queries/parent_field_ranking_spec.rb[1:1]
  • rspec ./spec/features/work_packages/table/switch_types_spec.rb[1:1:1]
  • rspec ./spec/features/work_packages/table/switch_types_spec.rb[1:1:2]
🤖 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 #24781, linked for reference only):

- `rspec ./spec/features/work_packages/table/queries/parent_field_ranking_spec.rb[1:1]`
- `rspec ./spec/features/work_packages/table/switch_types_spec.rb[1:1:1]`
- `rspec ./spec/features/work_packages/table/switch_types_spec.rb[1:1:2]`

Treat this as a standalone task, unrelated to PR #24781. Create a new branch from origin/dev and open a new pull request targeting dev — do not stack it on PR #24781 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 force-pushed the implementation/AGILE-364-batch-action-menus branch from 26d44ad to 8d11d33 Compare September 1, 2026 20:53
@myabc
myabc force-pushed the implementation/AGILE-364-batch-action-menus branch from 8d11d33 to 0503d7b Compare September 4, 2026 10:06
@myabc
myabc force-pushed the implementation/AGILE-364-batch-action-menus branch from 0503d7b to 94a4008 Compare September 5, 2026 16:24
Every sortable-lists consumer offers the same four directional moves,
but each surface hand-maintains its own labels, icons and direction
values. Introduces one descriptor so the vocabulary has a single home
and Backlogs renders from it.

The vocabulary carries the acts_as_list `move_to` value alongside the
relative direction, so the surfaces that still persist server-side can
adopt the table before they move onto the `prev_id` wire.

https://community.openproject.org/wp/DREAM-775
Resolves each action menu scope before presentation so contextual and
More-button invocation share one synchronous selection policy. The item
projects availability from that settled scope while retaining
non-mutating late refreshes for deferred menu content.

https://community.openproject.org/wp/AGILE-364
Locks all card-menu entry points to one observable action-scope
contract so presenter changes cannot silently collapse or retarget
selected batches.

https://community.openproject.org/wp/AGILE-364
Projects the settled action scope before exposing deferred batch
destinations and position controls, so a fixed invoker cannot present
stale batch actions the server would refuse.

https://community.openproject.org/wp/AGILE-364
Presents either the invoker's own actions or the batch actions, never
both: UI/UX review rejected the sectioned menu that showed headings
over both groups at once. A group toggle replaces the headings, keyed
to how many actions the menu presents rather than how many are
executable, so disable-mode consumers keep their items on screen.

The batch context moves into the invoker's tooltip, whose text is the
button's accessible name through Primer's aria-labelledby wiring. The
rename applies only while the menu is open and reverts on close; the
singular name arrives as a server-rendered value, since the tooltip's
own text is no restore source once a Turbo snapshot captures it
mid-rename, and connecting restores it over such a snapshot.

Reopening an already-loaded menu needs its own focus correction:
Primer's open-time pick checks [hidden] one level deep, so it still
matches the first singular item under a hidden group and focusing it
no-ops. Focus also parks on a presented batch item before its former
group hides, because Primer dismisses the menu the moment focus
leaves it.

The shared dismiss helper closes menus with Escape: the compact batch
menu can leave a live menuitem under the overlay's centre point, so
the old outside click activated an action instead of dismissing.

https://community.openproject.org/wp/AGILE-364
Keeps the inherited selection-persistence scenario on direct card
navigation now that opening an unselected action menu intentionally
settles a new one-card action scope.

https://community.openproject.org/wp/AGILE-364
Primer's toggle lifecycle, group and divider visibility, menu naming and
open-time focus repair had grown into a second state machine inside the
controller that owns sortable behaviour. A plain collaborator wrapping
the ActionMenu element keeps each of them answerable on its own.

Naming moves with it, and gains the menu itself: Primer labels the list
by its invoker button, whose own name comes from a further
aria-labelledby, and those references do not chain.

https://community.openproject.org/wp/AGILE-364
The menu took its batch name from an I18n key and its singular name from
a literal string the component read out of another key, so the two
scopes were named by different mechanisms and could drift apart.

One key with plural forms names both: `one` is the menu of a single
card, which is also what a batch of one presents.

https://community.openproject.org/wp/AGILE-364
Escape dismissal races the dialog a menu item is still loading. The
modal guard reads the DOM before showModal, so the key arrives after
it and closes the dialog, which the dialog stream action then removes
from the DOM. The spec sees a menu action that opened nothing.

Primer already hides the menu when an item is activated, so the click
path only waits for that instead of forcing the key. Menu inspection
keeps the Escape dismissal, and so does the one invocation whose
response morphs the card list out from under the open menu.

Reproduced by holding the dismissal until the dialog opens, which
fails every run before this change and passes after.

https://community.openproject.org/wp/AGILE-364
@myabc
myabc force-pushed the implementation/AGILE-364-batch-action-menus branch from 94a4008 to d65431a Compare September 5, 2026 16:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature javascript Pull requests that update Javascript code needs review ruby Pull requests that update Ruby code styling

Development

Successfully merging this pull request may close these issues.

3 participants