Skip to content

[74625] Make project hierarchy collapsable in the global project selector#23137

Merged
HDinger merged 12 commits into
devfrom
feature/74625-make-project-hierarchy-collapsable-in-the-global-project-selector
Jun 8, 2026
Merged

[74625] Make project hierarchy collapsable in the global project selector#23137
HDinger merged 12 commits into
devfrom
feature/74625-make-project-hierarchy-collapsable-in-the-global-project-selector

Conversation

@HDinger

@HDinger HDinger commented May 8, 2026

Copy link
Copy Markdown
Contributor

Warning

this relies on the changes from opf/primer_view_components#451

Ticket

https://community.openproject.org/wp/74625

What are you trying to accomplish?

Features

  • Use new async FilterableTreeView for global project selector
  • Exisiting behavior is kept:
    • "All|Favourite" selector
    • Stars are shown behind favourited projects
    • Current project can be deselected via a small cross
    • The limit of 300 projects + current project and its hierarchy is kept

Housekeeping

  • Update dependencies
  • Document changes to FilterableTreeView
  • Delete old Angular component and strings

Screenshots

Bildschirmfoto 2026-05-11 um 15 10 57

@HDinger HDinger added this to the 17.5.x milestone May 8, 2026
@HDinger HDinger added the feature label May 8, 2026
@HDinger HDinger marked this pull request as draft May 8, 2026 12:38
@github-actions

github-actions Bot commented May 9, 2026

Copy link
Copy Markdown

Deploying openproject with PullPreview

Field Value
Latest commit 0627cd4
Job deploy
Status ✅ Deploy successful
Preview URL https://pr-23137-74625-make-proje-ip-167-235-250-91.my.opf.run:443

View logs

@HDinger HDinger force-pushed the feature/74625-make-project-hierarchy-collapsable-in-the-global-project-selector branch 2 times, most recently from f542b25 to f78ae09 Compare May 11, 2026 13:09
@HDinger

HDinger commented May 11, 2026

Copy link
Copy Markdown
Contributor Author

@lindenthal The PullPreview will not work here (yet) since the changes are based on the local changes to the FilterableTreeView component. Since that is not released yet, this PR doesn't know anything about the async loading and will just fail.
I did that to see whether my changes to the FilterableTreeView work in production as I anticapted. Once the Primer changes are merged and release, the PullPreview will work here as well.

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

Replaces the Angular-based global project selector (opce-header-project-select) with a server-rendered ViewComponent (Header::ProjectSelectComponent) backed by the new async Primer::OpenProject::FilterableTreeView. A new Header::ProjectsController#index endpoint returns the tree fragment (visible, active projects ordered by lft, with the current project's hierarchy preserved and a 300-row cap), and the obsolete Angular component, list, sass, HTML, helper, and locale keys are deleted. A small Stimulus controller persists the "all/favorites" filter mode in localStorage, and the FilterableTreeView lookbook docs are extended to document async mode and the new select_variant: :none (anchor) usage. Main-menu sass is tweaked so the embedded TreeView is not over-styled by the menu rules.

Changes:

  • Introduce backend Header::ProjectSelectComponent + Header::ProjectsController and node partials returning a Primer::Alpha::TreeView fragment for an async FilterableTreeView.
  • Remove the Angular OpHeaderProjectSelect* component, related sass/HTML, insert-in-list helper, and obsolete js.label_project_list/include_workspaces.search_placeholder locale keys; add new header_project_select.* keys.
  • Add a Stimulus controller to persist filter-mode selection, scope main-menu sass to avoid styling TreeView, and document async FilterableTreeView mode + select_variant: :none.

Reviewed changes

Copilot reviewed 27 out of 28 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
lib/redmine/menu_manager/top_menu/projects_menu.rb Render new ViewComponent instead of Angular custom element.
app/components/header/project_select_component.{rb,html.erb,sass} New Primer Overlay-based project select trigger and body.
app/controllers/header/projects_controller.rb New endpoint returning a tree fragment of visible projects (with filter/search).
app/views/header/projects/index.html.erb, _node.html.erb Tree fragment + recursive node partial with anchor nodes, star/leave icons.
config/routes.rb Add namespace :header { resources :projects, only: :index }.
config/locales/en.yml, config/locales/js-en.yml Add header_project_select.* keys; remove obsolete js.label_project_* and include_workspaces.search_placeholder.
frontend/src/stimulus/controllers/header-project-select.controller.ts, setup.ts Persist last filter mode in localStorage and re-apply it on open.
frontend/src/global_styles/layout/_main_menu.sass Scope generic ul/a rules so they don't apply to TreeView.
frontend/src/global_styles/primer/_overrides.sass Layout tweak for the project select FilterableTreeView body.
frontend/src/app/shared/components/header-project-select/** Delete Angular component, template, sass, list, insert-in-list helper.
frontend/src/app/shared/components/grids/widgets/project-favorites/* Move "no favorites" styles to the widget that still uses them.
frontend/src/app/app.module.ts Drop registration of the deleted Angular component and custom element.
frontend/package-lock.json Updated @primer/behaviors, @github/include-fragment-element, @github/catalyst and license metadata.
lookbook/docs/components/tree-view/filterable-tree-view.md.erb Document async (src:) mode and select_variant: :single/:none.
spec/features/menu_items/top_menu_item_spec.rb Update I18n keys used by the test (see review comment).
app/components/_index.sass Import the new component's sass.
Files not reviewed (1)
  • frontend/package-lock.json: Language not supported

Comment thread spec/features/menu_items/top_menu_item_spec.rb Outdated
Comment on lines +36 to +41
private onFilterModeClick = (event:MouseEvent):void => {
const button = (event.target as HTMLElement).closest<HTMLElement>('[data-name]');
if (button?.dataset.name) {
localStorage.setItem(STORAGE_KEY, button.dataset.name);
}
};

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.

It took me a while to understand this comment, but I believe the issue is that any nested element inside the project selector that renders a data-name attribute (a pretty common attribute name), could interfere with the implementation.

Comment thread app/controllers/header/projects_controller.rb

@myabc myabc 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've only looked at the visual changes - not the code yet.

The focus ring on the toggle button is clipped:

Image

Missing padding between the tree item and right panel edge:

Image

There is still the gap between the tree items and the edge of the panel (this is an existing error):

Image

@HDinger HDinger force-pushed the feature/74625-make-project-hierarchy-collapsable-in-the-global-project-selector branch 5 times, most recently from f354e8a to dd3bf70 Compare May 21, 2026 13:37
@lindenthal

Copy link
Copy Markdown
Member

hi @HDinger,
Did you consider to use full width on mobile?

image

@HDinger HDinger force-pushed the feature/74625-make-project-hierarchy-collapsable-in-the-global-project-selector branch 3 times, most recently from 9bcc8c4 to cb24cc9 Compare May 22, 2026 11:55
Comment thread frontend/src/stimulus/controllers/header-project-select.controller.ts Outdated
Comment thread app/components/header/project_select_component.html.erb Outdated

@myabc myabc 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.

This works well - and "feels" snappy. 💯

That said, given what a critical part of the UI this is, I've striven to be extra stringent in this review.

Inline comments that I consider especially important have been marked with a ⚠️. Here is a summary:

  • visibility leak in merge_with_ancestors
  • favourites mode breaking hierarchy
  • lack of guards on localStorage
  • removal of the project menu keyboard shortcut (i.e. accesskey="5")

Also very important, but not mentioned directly inline:

  • eager loading of projects: tree-view and the overlay are now loaded on every page. Previously, an API call to /api/v3/projects was only made on opening the project selector.
Image

Those issues don't necessarily need to block this PR, but we should be prepared to handle any performance regressions.

Some things I'm not fond of:

  • lack of specs: this is my biggest concern. There are no controller/request specs in this PR. While the feature specs are good enough to verify the basic functionality, we need unit-style specs to iron out the edge cases and make it easier for future devs to verify fixes for the inevitable regressions.
  • complexity in the views, e.g. _node.html.erb. Also our reliance on helpers. I don't have a good solution for this though.
  • amount of logic in the controllers

Some suggestions: (for either this PR or a follow-up)

  1. extract tree-building into a serviceHeader::ProjectTreeBuilder with #call(projects, current_project_id:, matching_ids:) returning the nested structure. Makes it unit-testable without controller overhead and keeps the controller thin. (I can imagine @ulferts also wanting this 😄 )
  2. server-side filter mode via cookie/session — Instead of localStorage (with all its pitfalls), persist filter mode server-side. The Stimulus controller becomes zero lines.
  3. request specs for Header::ProjectsController. Given this endpoint serves every page load, it deserves:
    • Visibility filtering (user sees only permitted projects)
    • Ancestor inclusion (search + favourites)
    • Anonymous user behaviour
    • current_project_id edge cases (0, nil, invalid, archived)
    • The 300-project cap behavior

Claude also came up with this suggestion, which I'd file under "future optimisations"

  1. Consider collection rendering for _node.html.erb — If tree depth is bounded (which it effectively is at 300 nodes), a flat iteration with depth tracking could replace recursive partial rendering, reducing ERB overhead.

Comment thread app/components/header/project_select_component.html.erb Outdated
anchor_side: :outside_bottom,
anchor_align: :start,
size: :medium_portrait,
padding: :none

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.

Why no right padding?

Image

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.

What browser is that? The FilterableTreeViewTreeContainer should itself add some right side padding via the scrollbar-gutter

Bildschirmfoto 2026-06-02 um 08 39 05

Comment thread app/components/header/projects/node_component.html.erb
@myabc

myabc commented May 27, 2026

Copy link
Copy Markdown
Contributor

@HDinger regarding this comment:

Further, there is also a BIM test failing constantly which I did not figure out yet.

Do you have a local BIM environment to reproduce this?

Comment thread app/controllers/header/projects_controller.rb Outdated
Comment thread app/controllers/header/projects_controller.rb Outdated
Comment thread app/components/header/project_select_component.html.erb Outdated
Comment thread app/controllers/header/projects_controller.rb Outdated
Comment thread app/controllers/header/projects_controller.rb Outdated
Comment thread app/views/header/projects/_node.html.erb Outdated
Comment thread app/components/header/project_select_component.html.erb Outdated
Comment thread app/components/header/project_select_component.html.erb Outdated
…ject-hierarchy-collapsable-in-the-global-project-selector
@HDinger HDinger force-pushed the feature/74625-make-project-hierarchy-collapsable-in-the-global-project-selector branch 2 times, most recently from 5c181e0 to 93f6639 Compare June 2, 2026 07:26
@HDinger HDinger force-pushed the feature/74625-make-project-hierarchy-collapsable-in-the-global-project-selector branch from 93f6639 to 34d9306 Compare June 2, 2026 09:01
@HDinger HDinger force-pushed the feature/74625-make-project-hierarchy-collapsable-in-the-global-project-selector branch from 34d9306 to 628bb85 Compare June 2, 2026 10:16
…new project selector changes made it so bad that the test broke because the plus icon was overlapping the checkbox
@HDinger HDinger requested a review from myabc June 2, 2026 12:23
…ject-hierarchy-collapsable-in-the-global-project-selector

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

Files not reviewed (1)
  • frontend/package-lock.json: Language not supported

Comment thread spec/controllers/header/projects_controller_spec.rb Outdated
Comment thread lookbook/docs/components/tree-view/filterable-tree-view.md.erb Outdated

@myabc myabc 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.

Codewise this looks good.

@myabc myabc self-requested a review June 5, 2026 11:07
@lindenthal

lindenthal commented Jun 5, 2026

Copy link
Copy Markdown
Member

Hi @HDinger and @myabc,

Many thanks for working on this.

While I was playing around with it on Pullpreview I realized that I missed a feature in my specification:

When filtering for favorite projects I can not see them if they are not on root level. The favorite projects are hidden below their parent projects. Is it possible to show the favorite projects when filtering for them? This would be similar to entering a search term.

IMG_3154

The second minor thing is the deselect x-Icon. It is a bit misplaced.

A third question: do you see an easy way to unfold all project hierarchies?

Many thanks
Niels

@HDinger

HDinger commented Jun 8, 2026

Copy link
Copy Markdown
Contributor Author

Hi @lindenthal

When filtering for favorite projects I can not see them if they are not on root level. The favorite projects are hidden below their parent projects. Is it possible to show the favorite projects when filtering for them? This would be similar to entering a search term.

Thanks for the feedback. This is very interesting as I could not reproduce locally, but on the PullPreview instance. I will double check what happened.

The second minor thing is the deselect x-Icon. It is a bit misplaced.

Yes, we are aware of that and decided to tackle in a follow-up PR 👍

A third question: do you see an easy way to unfold all project hierarchies?

There are ways to quite easily re-render the whole content of the dropdown with expanded nodes. I guess the challenge is rather in the details like keeping the search term and the current scroll position.

@HDinger HDinger merged commit 396de93 into dev Jun 8, 2026
22 of 23 checks passed
@HDinger HDinger deleted the feature/74625-make-project-hierarchy-collapsable-in-the-global-project-selector branch June 8, 2026 06:14
@github-actions github-actions Bot locked and limited conversation to collaborators Jun 8, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

Development

Successfully merging this pull request may close these issues.

4 participants