From 9be85a53e4af6fc634c801883ceda6b8de02cbbc Mon Sep 17 00:00:00 2001
From: Dombi Attila <83396+dombesz@users.noreply.github.com>
Date: Fri, 7 Feb 2025 23:21:03 +0200
Subject: [PATCH 01/16] Fix perceived complexity error
---
spec/support/pages/projects/index.rb | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/spec/support/pages/projects/index.rb b/spec/support/pages/projects/index.rb
index 52fa4beb5b14..c73c03fc4f0d 100644
--- a/spec/support/pages/projects/index.rb
+++ b/spec/support/pages/projects/index.rb
@@ -296,7 +296,7 @@ def autocomplete_options_for(custom_field)
visible_user_auto_completer_options
end
- def apply_operator(name, human_operator)
+ def apply_operator(human_operator, name)
select(human_operator, from: "operator") unless boolean_filter?(name)
end
From 2bf950fccd02bbcd73f99fed734248957b4249e1 Mon Sep 17 00:00:00 2001
From: Dombi Attila <83396+dombesz@users.noreply.github.com>
Date: Tue, 11 Feb 2025 12:17:12 +0200
Subject: [PATCH 02/16] [#61398] Version autocompleter for filter values on the
project list
https://community.openproject.org/work_packages/61398
---
app/components/filter/filter_component.rb | 24 +++++--
app/models/custom_field.rb | 68 ++++++++++---------
.../op-autocompleter.component.html | 5 +-
.../op-autocompleter.component.sass | 2 +
4 files changed, 61 insertions(+), 38 deletions(-)
diff --git a/app/components/filter/filter_component.rb b/app/components/filter/filter_component.rb
index 5412a4e34c2a..28d1699cd3a9 100644
--- a/app/components/filter/filter_component.rb
+++ b/app/components/filter/filter_component.rb
@@ -70,8 +70,9 @@ def additional_filter_attributes(filter)
{ autocomplete_options: project_autocomplete_options }
when Queries::Filters::Shared::CustomFields::User
{ autocomplete_options: user_autocomplete_options }
- when Queries::Filters::Shared::CustomFields::ListOptional,
- Queries::Projects::Filters::ProjectStatusFilter,
+ when Queries::Filters::Shared::CustomFields::ListOptional
+ { autocomplete_options: custom_field_list_autocomplete_options(filter) }
+ when Queries::Projects::Filters::ProjectStatusFilter,
Queries::Projects::Filters::TypeFilter
{ autocomplete_options: list_autocomplete_options(filter) }
else
@@ -79,11 +80,26 @@ def additional_filter_attributes(filter)
end
end
+ def custom_field_list_autocomplete_options(filter)
+ items = if filter.custom_field.field_format == "version"
+ filter.allowed_values.map { |name, id, project_name| { name:, id:, project_name: } }
+ else
+ filter.allowed_values.map { |name, id| { name:, id: } }
+ end
+
+ autocomplete_options.merge(items:, model: filter.values)
+ end
+
def list_autocomplete_options(filter)
+ autocomplete_options.merge(
+ items: filter.allowed_values.map { |name, id| { name:, id: } },
+ model: filter.values
+ )
+ end
+
+ def autocomplete_options
{
component: "opce-autocompleter",
- items: filter.allowed_values.map { |name, id| { name:, id: } },
- model: filter.values,
bindValue: "id",
bindLabel: "name",
hideSelected: true
diff --git a/app/models/custom_field.rb b/app/models/custom_field.rb
index 426aebfd36a7..cce8bfcbbbe9 100644
--- a/app/models/custom_field.rb
+++ b/app/models/custom_field.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) the OpenProject GmbH
@@ -315,30 +317,36 @@ def cache_key
private
def possible_version_values_options(obj)
- mapped_with_deduced_project(obj) do |project|
- if project&.persisted?
- project.shared_versions
- else
- Version.systemwide
- end
- end
+ project = deduce_project(obj)
+
+ versions = if project&.persisted?
+ project.shared_versions
+ else
+ Version.systemwide
+ end
+
+ versions.includes(:project)
+ .sort
+ .map { |u| [u.name, u.id.to_s, u.project.name] }
end
def possible_user_values_options(obj)
- mapped_with_deduced_project(obj) do |project|
- scope = if project&.persisted?
- project.principals
- else
- Principal
- .in_visible_project_or_me(User.current)
- end
-
- user_format_columns = User::USER_FORMATS_STRUCTURE[Setting.user_format].map(&:to_s)
- # Always include lastname if not already included, as Groups always need a lastname (alias for name)
- user_format_columns << "lastname" unless user_format_columns.include?("lastname")
-
- scope.select(*user_format_columns, "id", "type")
- end
+ project = deduce_project(obj)
+
+ users = if project&.persisted?
+ project.principals
+ else
+ Principal
+ .in_visible_project_or_me(User.current)
+ end
+
+ user_format_columns = User::USER_FORMATS_STRUCTURE[Setting.user_format].map(&:to_s)
+ # Always include lastname if not already included, as Groups always need a lastname (alias for name)
+ user_format_columns << "lastname" unless user_format_columns.include?("lastname")
+
+ users.select(*user_format_columns, "id", "type")
+ .sort
+ .map { |u| [u.name, u.id.to_s] }
end
def possible_list_values_options
@@ -353,18 +361,12 @@ def possible_values_from_arg(arg)
end
end
- def mapped_with_deduced_project(project)
- project = if project.is_a?(Project)
- project
- elsif project.respond_to?(:project)
- project.project
- end
-
- result = yield project
-
- result
- .sort
- .map { |u| [u.name, u.id.to_s] }
+ def deduce_project(project)
+ if project.is_a?(Project)
+ project
+ elsif project.respond_to?(:project)
+ project.project
+ end
end
def destroy_help_text
diff --git a/frontend/src/app/shared/components/autocompleter/op-autocompleter/op-autocompleter.component.html b/frontend/src/app/shared/components/autocompleter/op-autocompleter/op-autocompleter.component.html
index 9b44f22c02a2..921ef7563e4d 100644
--- a/frontend/src/app/shared/components/autocompleter/op-autocompleter/op-autocompleter.component.html
+++ b/frontend/src/app/shared/components/autocompleter/op-autocompleter/op-autocompleter.component.html
@@ -183,7 +183,6 @@
[ngClass]="highlighting('status',item.status?.id)"
class="op-autocompleter--wp-status"
>
-
@@ -218,6 +217,10 @@
class="op-autocompleter__option-principal-email"
*ngIf="item.email"
[ngOptionHighlight]="search">{{ item.email }}
+ ({{ item.project_name }})
diff --git a/frontend/src/app/shared/components/autocompleter/op-autocompleter/op-autocompleter.component.sass b/frontend/src/app/shared/components/autocompleter/op-autocompleter/op-autocompleter.component.sass
index b8022bb77474..e9fc8981b342 100644
--- a/frontend/src/app/shared/components/autocompleter/op-autocompleter/op-autocompleter.component.sass
+++ b/frontend/src/app/shared/components/autocompleter/op-autocompleter/op-autocompleter.component.sass
@@ -50,12 +50,14 @@
text-overflow: ellipsis
&__option-principal-email
+ &__option-project-name
color: var(--fgColor-muted)
font-size: var(--font-size-small)
margin-left: var(--stack-gap-condensed)
@media screen and (max-width: $breakpoint-sm)
&__option-principal-email
+ &__option-project-name
display: block
margin-left: 0
margin-top: var(--control-xsmall-gap)
From cae99b53c0e67bd635a54d04d6f40f70f6732279 Mon Sep 17 00:00:00 2001
From: Dombi Attila <83396+dombesz@users.noreply.github.com>
Date: Mon, 17 Feb 2025 23:30:32 +0200
Subject: [PATCH 03/16] Refactor complex method
---
app/models/custom_field.rb | 43 +++++++++++++++++++++++---------------
1 file changed, 26 insertions(+), 17 deletions(-)
diff --git a/app/models/custom_field.rb b/app/models/custom_field.rb
index cce8bfcbbbe9..eddd0997e48b 100644
--- a/app/models/custom_field.rb
+++ b/app/models/custom_field.rb
@@ -318,12 +318,7 @@ def cache_key
def possible_version_values_options(obj)
project = deduce_project(obj)
-
- versions = if project&.persisted?
- project.shared_versions
- else
- Version.systemwide
- end
+ versions = deduce_versions(project)
versions.includes(:project)
.sort
@@ -332,17 +327,7 @@ def possible_version_values_options(obj)
def possible_user_values_options(obj)
project = deduce_project(obj)
-
- users = if project&.persisted?
- project.principals
- else
- Principal
- .in_visible_project_or_me(User.current)
- end
-
- user_format_columns = User::USER_FORMATS_STRUCTURE[Setting.user_format].map(&:to_s)
- # Always include lastname if not already included, as Groups always need a lastname (alias for name)
- user_format_columns << "lastname" unless user_format_columns.include?("lastname")
+ users = deduce_principals(project)
users.select(*user_format_columns, "id", "type")
.sort
@@ -369,6 +354,30 @@ def deduce_project(project)
end
end
+ def deduce_principals(project)
+ if project&.persisted?
+ project.principals
+ else
+ Principal
+ .in_visible_project_or_me(User.current)
+ end
+ end
+
+ def deduce_versions(project)
+ if project&.persisted?
+ project.shared_versions
+ else
+ Version.systemwide
+ end
+ end
+
+ def user_format_columns
+ user_format_columns = User::USER_FORMATS_STRUCTURE[Setting.user_format].map(&:to_s)
+ # Always include lastname if not already included, as Groups always need a lastname (alias for name)
+ user_format_columns << "lastname" unless user_format_columns.include?("lastname")
+ user_format_columns
+ end
+
def destroy_help_text
AttributeHelpText
.where(attribute_name:)
From 3e28d3ed536cfe4e85b0798ee19db6f88fdb17fc Mon Sep 17 00:00:00 2001
From: Dombi Attila <83396+dombesz@users.noreply.github.com>
Date: Tue, 18 Feb 2025 00:07:58 +0200
Subject: [PATCH 04/16] Fix specs
---
spec/models/custom_field_spec.rb | 19 ++++++++++++-------
1 file changed, 12 insertions(+), 7 deletions(-)
diff --git a/spec/models/custom_field_spec.rb b/spec/models/custom_field_spec.rb
index 88e7f98f98b5..51b4e378f5b6 100644
--- a/spec/models/custom_field_spec.rb
+++ b/spec/models/custom_field_spec.rb
@@ -289,20 +289,25 @@
end
context "for a version custom field" do
- let(:versions) { [build_stubbed(:version), build_stubbed(:version)] }
+ let(:versions) { [build_stubbed(:version, project:), build_stubbed(:version, project:)] }
+ let(:shared_versions_scope) { instance_double(ActiveRecord::Relation) }
before do
field.field_format = "version"
+ allow(shared_versions_scope)
+ .to receive(:includes)
+ .with(:project)
+ .and_return(versions)
end
context "with a project provided" do
it "returns the project's shared_versions" do
allow(project)
.to receive(:shared_versions)
- .and_return(versions)
+ .and_return(shared_versions_scope)
expect(field.possible_values_options(project))
- .to eql(versions.sort.map { |u| [u.name, u.id.to_s] })
+ .to eql(versions.sort.map { |u| [u.name, u.id.to_s, project.name] })
end
end
@@ -312,10 +317,10 @@
it "returns the project's shared_versions" do
allow(project)
.to receive(:shared_versions)
- .and_return(versions)
+ .and_return(shared_versions_scope)
expect(field.possible_values_options(project))
- .to eql(versions.sort.map { |u| [u.name, u.id.to_s] })
+ .to eql(versions.sort.map { |u| [u.name, u.id.to_s, project.name] })
end
end
@@ -323,10 +328,10 @@
it "returns the systemwide versions" do
allow(Version)
.to receive(:systemwide)
- .and_return(versions)
+ .and_return(shared_versions_scope)
expect(field.possible_values_options)
- .to eql(versions.sort.map { |u| [u.name, u.id.to_s] })
+ .to eql(versions.sort.map { |u| [u.name, u.id.to_s, project.name] })
end
end
end
From 65c0cea6b83a7c726fdddf3ace1ca3850b2636bf Mon Sep 17 00:00:00 2001
From: Dombi Attila <83396+dombesz@users.noreply.github.com>
Date: Wed, 19 Feb 2025 13:13:56 +0200
Subject: [PATCH 05/16] Fix scope related specs
---
spec/lib/custom_field_form_builder_spec.rb | 15 ++++++++++++---
.../custom_actions/actions/custom_field_spec.rb | 15 +++++++++++----
2 files changed, 23 insertions(+), 7 deletions(-)
diff --git a/spec/lib/custom_field_form_builder_spec.rb b/spec/lib/custom_field_form_builder_spec.rb
index 06337c5ed128..f5563b63efa3 100644
--- a/spec/lib/custom_field_form_builder_spec.rb
+++ b/spec/lib/custom_field_form_builder_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) the OpenProject GmbH
@@ -57,6 +59,8 @@
build_stubbed(:user)
end
+ let(:scope) { instance_double(ActiveRecord::Relation) }
+
before do
without_partial_double_verification do
allow(resource)
@@ -255,7 +259,6 @@
let(:project) { build_stubbed(:project) }
let(:user1) { build_stubbed(:user) }
let(:user2) { build_stubbed(:user) }
- let(:scope) { instance_double(ActiveRecord::Relation) }
let(:resource) { project }
@@ -273,8 +276,10 @@
.and_return(scope)
allow(scope)
- .to receive(:select)
- .and_return([user1, user2])
+ .to receive_messages(
+ select: [user1, user2],
+ includes: scope
+ )
end
it_behaves_like "wrapped in container", "select-container" do
@@ -331,6 +336,10 @@
allow(project)
.to receive(:shared_versions)
+ .and_return(scope)
+
+ allow(scope)
+ .to receive(:includes)
.and_return([version1, version2])
end
end
diff --git a/spec/models/custom_actions/actions/custom_field_spec.rb b/spec/models/custom_actions/actions/custom_field_spec.rb
index afc2c54b1f00..04ec6aaf8ce7 100644
--- a/spec/models/custom_actions/actions/custom_field_spec.rb
+++ b/spec/models/custom_actions/actions/custom_field_spec.rb
@@ -29,6 +29,7 @@
require_relative "../shared_expectations"
RSpec.describe CustomActions::Actions::CustomField do
+ let(:scope) { instance_double(ActiveRecord::Relation) }
let(:list_custom_field) do
build_stubbed(:list_wp_custom_field,
custom_options: [build_stubbed(:custom_option, value: "A"),
@@ -406,9 +407,13 @@
let(:versions) { [z_version, a_version, m_version] }
before do
+ allow(scope)
+ .to receive(:includes)
+ .and_return(versions)
+
allow(Version)
.to receive(:systemwide)
- .and_return(versions)
+ .and_return(scope)
end
context "for a non required field" do
@@ -441,7 +446,6 @@
build_stubbed(:user),
build_stubbed(:user)]
end
- let(:scope) { instance_double(ActiveRecord::Relation) }
before do
allow(Principal)
@@ -520,7 +524,6 @@
build_stubbed(:user),
build_stubbed(:user)]
end
- let(:scope) { instance_double(ActiveRecord::Relation) }
before do
allow(Principal)
@@ -551,9 +554,13 @@
end
before do
+ allow(scope)
+ .to receive(:includes)
+ .and_return(versions)
+
allow(Version)
.to receive(:systemwide)
- .and_return(versions)
+ .and_return(scope)
end
it_behaves_like "associated custom action validations" do
From e17bb32119302577cca8d4a3879af481d0c1cd87 Mon Sep 17 00:00:00 2001
From: Dombi Attila <83396+dombesz@users.noreply.github.com>
Date: Fri, 21 Feb 2025 15:55:23 +0200
Subject: [PATCH 06/16] Use include_blank options on the work package bulk edit
form
---
app/views/work_packages/bulk/edit.html.erb | 46 +++++++++++++++-------
1 file changed, 31 insertions(+), 15 deletions(-)
diff --git a/app/views/work_packages/bulk/edit.html.erb b/app/views/work_packages/bulk/edit.html.erb
index 7a682f26de31..10018db61eaf 100644
--- a/app/views/work_packages/bulk/edit.html.erb
+++ b/app/views/work_packages/bulk/edit.html.erb
@@ -55,14 +55,22 @@ See COPYRIGHT and LICENSE files for more details.
<% if @available_statuses.any? %>
<% else %>
@@ -76,16 +84,21 @@ See COPYRIGHT and LICENSE files for more details.
@@ -93,9 +106,10 @@ See COPYRIGHT and LICENSE files for more details.
<%= styled_label_tag :work_package_responsible_id, WorkPackage.human_attribute_name(:responsible) %>
<%= styled_select_tag(
- "work_package[responsible_id]", content_tag("option", t(:label_no_change_option), value: "") +
- content_tag("option", t(:label_nobody), value: "none") +
- options_from_collection_for_select(@responsibles, :id, :name)
+ "work_package[responsible_id]",
+ content_tag("option", t(:label_nobody), value: "none") +
+ options_from_collection_for_select(@responsibles, :id, :name),
+ include_blank: t(:label_no_change_option)
) %>
@@ -104,9 +118,10 @@ See COPYRIGHT and LICENSE files for more details.
<%= styled_label_tag :work_package_category_id, WorkPackage.human_attribute_name(:category) %>
<%= styled_select_tag(
- "work_package[category_id]", content_tag("option", t(:label_no_change_option), value: "") +
- content_tag("option", t(:label_none), value: "none") +
- options_from_collection_for_select(@project.categories, :id, :name)
+ "work_package[category_id]",
+ content_tag("option", t(:label_none), value: "none") +
+ options_from_collection_for_select(@project.categories, :id, :name),
+ include_blank: t(:label_no_change_option)
) %>
@@ -115,9 +130,10 @@ See COPYRIGHT and LICENSE files for more details.
<%= styled_label_tag :work_package_version_id, WorkPackage.human_attribute_name(:version) %>
<%= styled_select_tag(
- "work_package[version_id]", content_tag("option", t(:label_no_change_option), value: "") +
- content_tag("option", t(:label_none), value: "none") +
- version_options_for_select(@project.shared_versions.with_status_open.order_by_semver_name)
+ "work_package[version_id]",
+ content_tag("option", t(:label_none), value: "none") +
+ version_options_for_select(@project.shared_versions.with_status_open.order_by_semver_name),
+ include_blank: t(:label_no_change_option)
) %>
From ab3633204efc4e668f154b36c59f14a4dabd39e8 Mon Sep 17 00:00:00 2001
From: Dombi Attila <83396+dombesz@users.noreply.github.com>
Date: Fri, 21 Feb 2025 18:38:06 +0200
Subject: [PATCH 07/16] Use include blank options on the custom fields helpers.
---
app/helpers/custom_fields_helper.rb | 50 +++++++++++++++++------------
1 file changed, 30 insertions(+), 20 deletions(-)
diff --git a/app/helpers/custom_fields_helper.rb b/app/helpers/custom_fields_helper.rb
index 122cd405541f..fa29aeb7d05e 100644
--- a/app/helpers/custom_fields_helper.rb
+++ b/app/helpers/custom_fields_helper.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) the OpenProject GmbH
@@ -88,18 +90,19 @@ def custom_field_tag(name, custom_value) # rubocop:disable Metrics/AbcSize,Metri
required: custom_field.is_required)
hidden_tag + checkbox_tag
when "list"
- blank_option = if custom_field.is_required? && custom_field.default_value.blank?
- "--- #{I18n.t(:actionview_instancetag_blank_option)} --- "
- elsif custom_field.is_required? && custom_field.default_value.present?
- ""
- else
- " "
- end
-
- options = blank_option.html_safe + options_for_select(custom_field.possible_values_options(custom_value.customized),
- custom_value.value)
-
- styled_select_tag(field_name, options, id: field_id, container_class: "-middle", required: custom_field.is_required)
+ include_blank = !custom_field.is_required? ||
+ (custom_field.default_value.blank? ? I18n.t(:actionview_instancetag_blank_option) : false)
+
+ options = [custom_field.possible_values_options(custom_value.customized), custom_value.value]
+
+ styled_select_tag(
+ field_name,
+ options_for_select(*options),
+ id: field_id,
+ container_class: "-middle",
+ required: custom_field.is_required,
+ include_blank:
+ )
else
styled_text_field_tag(field_name, custom_value.value, id: field_id, container_class: "-middle",
required: custom_field.is_required)
@@ -158,12 +161,14 @@ def custom_field_tag_for_bulk_edit(name, custom_field, project = nil) # rubocop:
when "text"
styled_text_area_tag(field_name, "", id: field_id, rows: 3, with_text_formatting: true)
when "bool"
- styled_select_tag(field_name, options_for_select([[I18n.t(:label_no_change_option), ""],
- ([I18n.t(:label_none), "none"] unless custom_field.required?),
- [I18n.t(:general_text_yes), "1"],
- [I18n.t(:general_text_no), "0"]].compact), id: field_id)
+ styled_select_tag(field_name,
+ options_for_select([([I18n.t(:label_none), "none"] unless custom_field.required?),
+ [I18n.t(:general_text_yes), "1"],
+ [I18n.t(:general_text_no), "0"]].compact),
+ id: field_id,
+ include_blank: I18n.t(:label_no_change_option))
when "list"
- base_options = [[I18n.t(:label_no_change_option), ""]]
+ base_options = []
unless custom_field.required?
unset_label = custom_field.field_format == "user" ? :label_nobody : :label_none
base_options << [I18n.t(unset_label), "none"]
@@ -171,9 +176,10 @@ def custom_field_tag_for_bulk_edit(name, custom_field, project = nil) # rubocop:
styled_select_tag(field_name,
options_for_select(base_options + custom_field.possible_values_options(project)),
id: field_id,
- multiple: custom_field.multi_value?)
+ multiple: custom_field.multi_value?,
+ include_blank: I18n.t(:label_no_change_option))
when "hierarchy"
- base_options = [[I18n.t(:label_no_change_option), ""]]
+ base_options = []
result = CustomFields::Hierarchy::HierarchicalItemService.new
.get_descendants(item: custom_field.hierarchy_root, include_self: false)
.either(
@@ -184,7 +190,11 @@ def custom_field_tag_for_bulk_edit(name, custom_field, project = nil) # rubocop:
label = item.short.present? ? "#{item.label} (#{item.short})" : item.label
[label, item.id]
end
- styled_select_tag(field_name, options_for_select(options), id: field_id, multiple: custom_field.multi_value?)
+ styled_select_tag(field_name,
+ options_for_select(options),
+ id: field_id,
+ multiple: custom_field.multi_value?,
+ include_blank: I18n.t(:label_no_change_option))
else
styled_text_field_tag(field_name, "", id: field_id)
end
From 703a5212ca5c9c92db24b726a5cc1f5004aa9d20 Mon Sep 17 00:00:00 2001
From: Dombi Attila <83396+dombesz@users.noreply.github.com>
Date: Mon, 24 Feb 2025 15:18:19 +0200
Subject: [PATCH 08/16] Use version groups for the version html autocompleters.
---
app/components/filter/filter_component.rb | 2 +-
app/helpers/custom_fields_helper.rb | 85 +++--------------------
app/models/custom_field.rb | 30 ++++----
app/models/projects/versions.rb | 4 +-
lib/custom_field_form_builder.rb | 20 ++++--
5 files changed, 44 insertions(+), 97 deletions(-)
diff --git a/app/components/filter/filter_component.rb b/app/components/filter/filter_component.rb
index 28d1699cd3a9..755fb17fd5ab 100644
--- a/app/components/filter/filter_component.rb
+++ b/app/components/filter/filter_component.rb
@@ -81,7 +81,7 @@ def additional_filter_attributes(filter)
end
def custom_field_list_autocomplete_options(filter)
- items = if filter.custom_field.field_format == "version"
+ items = if filter.custom_field.version?
filter.allowed_values.map { |name, id, project_name| { name:, id:, project_name: } }
else
filter.allowed_values.map { |name, id| { name:, id: } }
diff --git a/app/helpers/custom_fields_helper.rb b/app/helpers/custom_fields_helper.rb
index fa29aeb7d05e..2bde1027da1a 100644
--- a/app/helpers/custom_fields_helper.rb
+++ b/app/helpers/custom_fields_helper.rb
@@ -64,76 +64,6 @@ def custom_fields_tabs
]
end
- # Return custom field html tag corresponding to its format
- def custom_field_tag(name, custom_value) # rubocop:disable Metrics/AbcSize,Metrics/PerceivedComplexity
- custom_field = custom_value.custom_field
- field_name = "#{name}[custom_field_values][#{custom_field.id}]"
- field_id = "#{name}_custom_field_values_#{custom_field.id}"
-
- field_format = OpenProject::CustomFieldFormat.find_by(name: custom_field.field_format)
-
- tag = case field_format.try(:edit_as)
- when "date"
- angular_component_tag "opce-basic-single-date-picker",
- inputs: {
- required: custom_field.is_required,
- value: custom_value.value,
- id: field_id,
- name: field_name
- }
- when "text"
- styled_text_area_tag(field_name, custom_value.value, id: field_id, rows: 3, container_class: "-middle",
- required: custom_field.is_required)
- when "bool"
- hidden_tag = hidden_field_tag(field_name, "0")
- checkbox_tag = styled_check_box_tag(field_name, "1", custom_value.typed_value, id: field_id,
- required: custom_field.is_required)
- hidden_tag + checkbox_tag
- when "list"
- include_blank = !custom_field.is_required? ||
- (custom_field.default_value.blank? ? I18n.t(:actionview_instancetag_blank_option) : false)
-
- options = [custom_field.possible_values_options(custom_value.customized), custom_value.value]
-
- styled_select_tag(
- field_name,
- options_for_select(*options),
- id: field_id,
- container_class: "-middle",
- required: custom_field.is_required,
- include_blank:
- )
- else
- styled_text_field_tag(field_name, custom_value.value, id: field_id, container_class: "-middle",
- required: custom_field.is_required)
- end
-
- tag = content_tag :span, tag, lang: custom_field.name_locale, class: "form--field-container"
-
- if custom_value.errors.empty?
- tag
- else
- ActionView::Base.wrap_with_error_span(tag, custom_value, "value")
- end
- end
-
- # Return custom field label tag
- def custom_field_label_tag(name, custom_value)
- content_tag "label", h(custom_value.custom_field.name) +
- (custom_value.custom_field.is_required? ? content_tag("span", " *", class: "required") : ""),
- for: "#{name}_custom_field_values_#{custom_value.custom_field.id}",
- class: "form--label #{custom_value.errors.empty? ? nil : 'error'}",
- lang: custom_value.custom_field.name_locale
- end
-
- def hidden_custom_field_label_tag(name, custom_value)
- content_tag "label", h(custom_value.custom_field.name) +
- (custom_value.custom_field.is_required? ? content_tag("span", " *", class: "required") : ""),
- for: "#{name}_custom_field_values_#{custom_value.custom_field.id}",
- class: "hidden-for-sighted",
- lang: custom_value.custom_field.name_locale
- end
-
def blank_custom_field_label_tag(name, custom_field)
content_tag "label", h(custom_field.name) +
(custom_field.is_required? ? content_tag("span", " *", class: "required") : ""),
@@ -141,11 +71,6 @@ def blank_custom_field_label_tag(name, custom_field)
class: "form--label"
end
- # Return custom field tag with its label tag
- def custom_field_tag_with_label(name, custom_value)
- custom_field_label_tag(name, custom_value) + custom_field_tag(name, custom_value)
- end
-
def custom_field_tag_for_bulk_edit(name, custom_field, project = nil) # rubocop:disable Metrics/AbcSize
field_name = "#{name}[custom_field_values][#{custom_field.id}]"
field_id = "#{name}_custom_field_values_#{custom_field.id}"
@@ -173,8 +98,16 @@ def custom_field_tag_for_bulk_edit(name, custom_field, project = nil) # rubocop:
unset_label = custom_field.field_format == "user" ? :label_nobody : :label_none
base_options << [I18n.t(unset_label), "none"]
end
+
+ possible_values = custom_field.possible_values_options(project)
+ options = if custom_field.version?
+ grouped_options_for_select(possible_values.group_by(&:last).to_a)
+ else
+ options_for_select(possible_values)
+ end
+
styled_select_tag(field_name,
- options_for_select(base_options + custom_field.possible_values_options(project)),
+ options_for_select(base_options) + options,
id: field_id,
multiple: custom_field.multi_value?,
include_blank: I18n.t(:label_no_change_option))
diff --git a/app/models/custom_field.rb b/app/models/custom_field.rb
index eddd0997e48b..cf84be290fb6 100644
--- a/app/models/custom_field.rb
+++ b/app/models/custom_field.rb
@@ -165,8 +165,10 @@ def value_of(value)
# You MUST NOT pass a customizable if this CF has any other format
def possible_values(obj = nil)
case field_format
- when "user", "version"
- possible_values_options(obj).map(&:last)
+ when "user"
+ possible_users(obj).pluck(:id).map(&:to_s)
+ when "version"
+ possible_versions(obj).pluck(:id).map(&:to_s)
when "list"
custom_options
else
@@ -316,22 +318,26 @@ def cache_key
private
- def possible_version_values_options(obj)
+ def possible_versions(obj)
project = deduce_project(obj)
- versions = deduce_versions(project)
+ deduce_versions(project)
+ end
- versions.includes(:project)
- .sort
- .map { |u| [u.name, u.id.to_s, u.project.name] }
+ def possible_version_values_options(obj)
+ possible_versions(obj).references(:project)
+ .sort
+ .map { |u| [u.name, u.id.to_s, u.project.name] }
end
- def possible_user_values_options(obj)
+ def possible_users(obj)
project = deduce_project(obj)
- users = deduce_principals(project)
+ deduce_principals(project)
+ end
- users.select(*user_format_columns, "id", "type")
- .sort
- .map { |u| [u.name, u.id.to_s] }
+ def possible_user_values_options(obj)
+ possible_users(obj).select(*user_format_columns, "id", "type")
+ .sort
+ .map { |u| [u.name, u.id.to_s] }
end
def possible_list_values_options
diff --git a/app/models/projects/versions.rb b/app/models/projects/versions.rb
index b271f8d6852c..d26e0661da3c 100644
--- a/app/models/projects/versions.rb
+++ b/app/models/projects/versions.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) the OpenProject GmbH
@@ -51,7 +53,7 @@ def shared_versions
Version.shared_with(self)
end
- # Returns all versions a work package can be assigned to. Opposed to
+ # Returns all versions a work package can be assigned to. Opposed to
# #shared_versions this returns an array of Versions, not a scope.
#
# The main benefit is in scenarios where work packages' projects are eager
diff --git a/lib/custom_field_form_builder.rb b/lib/custom_field_form_builder.rb
index 84e04e288399..43e84daa00b5 100644
--- a/lib/custom_field_form_builder.rb
+++ b/lib/custom_field_form_builder.rb
@@ -83,17 +83,23 @@ def custom_field_input(options = {})
end
end
- # rubocop:enable Metrics/AbcSize
-
def custom_field_input_list(field, input_options)
customized = Array(custom_value).first&.customized
- possible_options = custom_field.possible_values_options(customized)
- select_options = custom_field_select_options_for_object
- selected_options = Array(custom_value).map(&:value)
- selectable_options = template.options_for_select(possible_options, selected_options)
+ selectable_options = custom_field_input_list_options(customized, custom_value)
input_options[:multiple] = custom_field.multi_value?
- select(field, selectable_options, select_options, input_options).html_safe
+ select(field, selectable_options, custom_field_select_options_for_object, input_options)
+ end
+
+ def custom_field_input_list_options(customized, selected)
+ options = custom_field.possible_values_options(customized)
+ selected_options = Array(selected).map(&:value)
+
+ if custom_field.version?
+ template.grouped_options_for_select(options.group_by(&:last).to_a, selected_options)
+ else
+ template.options_for_select(options, selected_options)
+ end
end
def custom_field_select_options_for_object
From 65eb5a576383c097ed6c1aa838b0434761d12f1c Mon Sep 17 00:00:00 2001
From: Dombi Attila <83396+dombesz@users.noreply.github.com>
Date: Mon, 24 Feb 2025 17:19:10 +0200
Subject: [PATCH 09/16] Fix specs
---
lib/custom_field_form_builder.rb | 6 +++++-
spec/lib/custom_field_form_builder_spec.rb | 18 +++++++++++++-----
.../actions/custom_field_spec.rb | 14 +++++++++-----
spec/models/custom_field_spec.rb | 6 ++++--
4 files changed, 31 insertions(+), 13 deletions(-)
diff --git a/lib/custom_field_form_builder.rb b/lib/custom_field_form_builder.rb
index 43e84daa00b5..836361c992d8 100644
--- a/lib/custom_field_form_builder.rb
+++ b/lib/custom_field_form_builder.rb
@@ -96,7 +96,11 @@ def custom_field_input_list_options(customized, selected)
selected_options = Array(selected).map(&:value)
if custom_field.version?
- template.grouped_options_for_select(options.group_by(&:last).to_a, selected_options)
+ grouped_options = Hash.new { |hsh, key| hsh[key] = [] }
+ options.each do |label, value, group_key|
+ grouped_options[group_key] << [label, value]
+ end
+ template.grouped_options_for_select(grouped_options, selected_options)
else
template.options_for_select(options, selected_options)
end
diff --git a/spec/lib/custom_field_form_builder_spec.rb b/spec/lib/custom_field_form_builder_spec.rb
index f5563b63efa3..4eac6fc9a787 100644
--- a/spec/lib/custom_field_form_builder_spec.rb
+++ b/spec/lib/custom_field_form_builder_spec.rb
@@ -339,7 +339,7 @@
.and_return(scope)
allow(scope)
- .to receive(:includes)
+ .to receive(:references)
.and_return([version1, version2])
end
end
@@ -355,8 +355,12 @@
name="user[#{custom_field.id}]"
no_label="true">
- #{version1.name}
- #{version2.name}
+
+ #{version1.name}
+
+
+ #{version2.name}
+
}).at_path("select")
end
@@ -373,8 +377,12 @@
name="user[#{custom_field.id}]"
no_label="true">
--- Please select ---
- #{version1.name}
- #{version2.name}
+
+ #{version1.name}
+
+
+ #{version2.name}
+
}).at_path("select")
end
diff --git a/spec/models/custom_actions/actions/custom_field_spec.rb b/spec/models/custom_actions/actions/custom_field_spec.rb
index 04ec6aaf8ce7..644856e59880 100644
--- a/spec/models/custom_actions/actions/custom_field_spec.rb
+++ b/spec/models/custom_actions/actions/custom_field_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) the OpenProject GmbH
@@ -110,7 +112,7 @@
expect(described_class.all.map(&:custom_field))
.to match_array(custom_fields)
- described_class.all.each do |subclass|
+ described_class.find_each do |subclass|
expect(subclass.ancestors).to include(described_class)
end
end
@@ -408,7 +410,8 @@
before do
allow(scope)
- .to receive(:includes)
+ .to receive(:references)
+ .with(:project)
.and_return(versions)
allow(Version)
@@ -555,7 +558,8 @@
before do
allow(scope)
- .to receive(:includes)
+ .to receive(:references)
+ .with(:project)
.and_return(versions)
allow(Version)
@@ -577,8 +581,8 @@
it_behaves_like "bool custom action validations" do
let(:allowed_values) do
[
- { true: OpenProject::Database::DB_VALUE_TRUE },
- { false: OpenProject::Database::DB_VALUE_FALSE }
+ { true => OpenProject::Database::DB_VALUE_TRUE },
+ { false => OpenProject::Database::DB_VALUE_FALSE }
]
end
end
diff --git a/spec/models/custom_field_spec.rb b/spec/models/custom_field_spec.rb
index 51b4e378f5b6..56dde15fad23 100644
--- a/spec/models/custom_field_spec.rb
+++ b/spec/models/custom_field_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) the OpenProject GmbH
@@ -156,7 +158,7 @@
it "is not valid" do
expect(field)
- .to be_invalid
+ .not_to be_valid
end
end
@@ -295,7 +297,7 @@
before do
field.field_format = "version"
allow(shared_versions_scope)
- .to receive(:includes)
+ .to receive(:references)
.with(:project)
.and_return(versions)
end
From 187dd7ed7562d790801ba0da9acfde3f87cb6d95 Mon Sep 17 00:00:00 2001
From: Dombi Attila <83396+dombesz@users.noreply.github.com>
Date: Mon, 24 Feb 2025 21:50:49 +0200
Subject: [PATCH 10/16] Update list filter strategy to use the second value
from the allowed_values
---
app/components/filter/filter_component.rb | 11 +++++++----
app/models/queries/filters/strategies/list.rb | 4 ++--
.../op-autocompleter/op-autocompleter.component.html | 4 ----
.../op-autocompleter/op-autocompleter.component.sass | 2 --
4 files changed, 9 insertions(+), 12 deletions(-)
diff --git a/app/components/filter/filter_component.rb b/app/components/filter/filter_component.rb
index 755fb17fd5ab..dff5ad3a70b8 100644
--- a/app/components/filter/filter_component.rb
+++ b/app/components/filter/filter_component.rb
@@ -81,13 +81,16 @@ def additional_filter_attributes(filter)
end
def custom_field_list_autocomplete_options(filter)
- items = if filter.custom_field.version?
- filter.allowed_values.map { |name, id, project_name| { name:, id:, project_name: } }
+ options = if filter.custom_field.version?
+ {
+ items: filter.allowed_values.map { |name, id, project_name| { name:, id:, project_name: } },
+ groupBy: "project_name"
+ }
else
- filter.allowed_values.map { |name, id| { name:, id: } }
+ { items: filter.allowed_values.map { |name, id| { name:, id: } } }
end
- autocomplete_options.merge(items:, model: filter.values)
+ autocomplete_options.merge(options).merge(model: filter.values)
end
def list_autocomplete_options(filter)
diff --git a/app/models/queries/filters/strategies/list.rb b/app/models/queries/filters/strategies/list.rb
index 6476b0984fe4..dc5415ea95c2 100644
--- a/app/models/queries/filters/strategies/list.rb
+++ b/app/models/queries/filters/strategies/list.rb
@@ -52,11 +52,11 @@ def validate
end
def valid_values!
- filter.values &= (allowed_values.map(&:last).map(&:to_s) + ["-1"])
+ filter.values &= (allowed_values.map(&:second).map(&:to_s) + ["-1"])
end
def non_valid_values?
- (values.reject(&:blank?) & (allowed_values.map(&:last).map(&:to_s) + ["-1"])) != values.reject(&:blank?)
+ (values.reject(&:blank?) & (allowed_values.map(&:second).map(&:to_s) + ["-1"])) != values.reject(&:blank?)
end
end
end
diff --git a/frontend/src/app/shared/components/autocompleter/op-autocompleter/op-autocompleter.component.html b/frontend/src/app/shared/components/autocompleter/op-autocompleter/op-autocompleter.component.html
index 921ef7563e4d..a46c860ca180 100644
--- a/frontend/src/app/shared/components/autocompleter/op-autocompleter/op-autocompleter.component.html
+++ b/frontend/src/app/shared/components/autocompleter/op-autocompleter/op-autocompleter.component.html
@@ -217,10 +217,6 @@
class="op-autocompleter__option-principal-email"
*ngIf="item.email"
[ngOptionHighlight]="search">{{ item.email }}
- ({{ item.project_name }})
diff --git a/frontend/src/app/shared/components/autocompleter/op-autocompleter/op-autocompleter.component.sass b/frontend/src/app/shared/components/autocompleter/op-autocompleter/op-autocompleter.component.sass
index e9fc8981b342..b8022bb77474 100644
--- a/frontend/src/app/shared/components/autocompleter/op-autocompleter/op-autocompleter.component.sass
+++ b/frontend/src/app/shared/components/autocompleter/op-autocompleter/op-autocompleter.component.sass
@@ -50,14 +50,12 @@
text-overflow: ellipsis
&__option-principal-email
- &__option-project-name
color: var(--fgColor-muted)
font-size: var(--font-size-small)
margin-left: var(--stack-gap-condensed)
@media screen and (max-width: $breakpoint-sm)
&__option-principal-email
- &__option-project-name
display: block
margin-left: 0
margin-top: var(--control-xsmall-gap)
From 4f899436c0798f04a76d3edb26bd894fbd62dd1a Mon Sep 17 00:00:00 2001
From: Dombi Attila <83396+dombesz@users.noreply.github.com>
Date: Mon, 24 Feb 2025 22:02:29 +0200
Subject: [PATCH 11/16] Refactor filter allowed_values
---
app/components/filter/filter_component.rb | 14 +++++++-------
.../queries/filters/strategies/boolean_list.rb | 4 +++-
app/models/queries/filters/strategies/list.rb | 13 ++++++++-----
app/models/queries/filters/strategies/relation.rb | 4 +++-
4 files changed, 21 insertions(+), 14 deletions(-)
diff --git a/app/components/filter/filter_component.rb b/app/components/filter/filter_component.rb
index dff5ad3a70b8..a36e18f1c884 100644
--- a/app/components/filter/filter_component.rb
+++ b/app/components/filter/filter_component.rb
@@ -82,13 +82,13 @@ def additional_filter_attributes(filter)
def custom_field_list_autocomplete_options(filter)
options = if filter.custom_field.version?
- {
- items: filter.allowed_values.map { |name, id, project_name| { name:, id:, project_name: } },
- groupBy: "project_name"
- }
- else
- { items: filter.allowed_values.map { |name, id| { name:, id: } } }
- end
+ {
+ items: filter.allowed_values.map { |name, id, project_name| { name:, id:, project_name: } },
+ groupBy: "project_name"
+ }
+ else
+ { items: filter.allowed_values.map { |name, id| { name:, id: } } }
+ end
autocomplete_options.merge(options).merge(model: filter.values)
end
diff --git a/app/models/queries/filters/strategies/boolean_list.rb b/app/models/queries/filters/strategies/boolean_list.rb
index 133107415931..a43eca129563 100644
--- a/app/models/queries/filters/strategies/boolean_list.rb
+++ b/app/models/queries/filters/strategies/boolean_list.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) the OpenProject GmbH
@@ -37,7 +39,7 @@ def validate
end
def valid_values!
- filter.values &= allowed_values.map { |v| v.last.to_s }
+ filter.values &= allowed_values.map { |_, v| v.to_s }
end
private
diff --git a/app/models/queries/filters/strategies/list.rb b/app/models/queries/filters/strategies/list.rb
index dc5415ea95c2..0a4eaf4d76ce 100644
--- a/app/models/queries/filters/strategies/list.rb
+++ b/app/models/queries/filters/strategies/list.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) the OpenProject GmbH
@@ -28,9 +30,6 @@
module Queries::Filters::Strategies
class List < BaseStrategy
- delegate :allowed_values,
- to: :filter
-
self.supported_operators = ["=", "!"]
self.default_operator = "="
@@ -51,12 +50,16 @@ def validate
end
end
+ def allowed_values
+ filter.allowed_values.map { |_, v| v.to_s }
+ end
+
def valid_values!
- filter.values &= (allowed_values.map(&:second).map(&:to_s) + ["-1"])
+ filter.values &= (allowed_values + ["-1"])
end
def non_valid_values?
- (values.reject(&:blank?) & (allowed_values.map(&:second).map(&:to_s) + ["-1"])) != values.reject(&:blank?)
+ (values.compact_blank & (allowed_values + ["-1"])) != values.compact_blank
end
end
end
diff --git a/app/models/queries/filters/strategies/relation.rb b/app/models/queries/filters/strategies/relation.rb
index 236390d3294a..ef8109d0d802 100644
--- a/app/models/queries/filters/strategies/relation.rb
+++ b/app/models/queries/filters/strategies/relation.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) the OpenProject GmbH
@@ -48,7 +50,7 @@ def validate
end
def valid_values!
- filter.values &= allowed_values.map { |v| v.last.to_s }
+ filter.values &= allowed_values.map { |_, v| v.to_s }
end
private
From b78e17a22049747ab92fb44b11af559a4feeaba0 Mon Sep 17 00:00:00 2001
From: Dombi Attila <83396+dombesz@users.noreply.github.com>
Date: Mon, 24 Feb 2025 22:25:09 +0200
Subject: [PATCH 12/16] Fix perceived complexity error in custom fields helper.
---
app/helpers/custom_fields_helper.rb | 32 ++++++++++++++++-------------
1 file changed, 18 insertions(+), 14 deletions(-)
diff --git a/app/helpers/custom_fields_helper.rb b/app/helpers/custom_fields_helper.rb
index 2bde1027da1a..a7a81e5bdd27 100644
--- a/app/helpers/custom_fields_helper.rb
+++ b/app/helpers/custom_fields_helper.rb
@@ -93,21 +93,8 @@ def custom_field_tag_for_bulk_edit(name, custom_field, project = nil) # rubocop:
id: field_id,
include_blank: I18n.t(:label_no_change_option))
when "list"
- base_options = []
- unless custom_field.required?
- unset_label = custom_field.field_format == "user" ? :label_nobody : :label_none
- base_options << [I18n.t(unset_label), "none"]
- end
-
- possible_values = custom_field.possible_values_options(project)
- options = if custom_field.version?
- grouped_options_for_select(possible_values.group_by(&:last).to_a)
- else
- options_for_select(possible_values)
- end
-
styled_select_tag(field_name,
- options_for_select(base_options) + options,
+ options_for_list(custom_field, project),
id: field_id,
multiple: custom_field.multi_value?,
include_blank: I18n.t(:label_no_change_option))
@@ -164,4 +151,21 @@ def label_for_custom_field_format(format_string)
"#{label}#{suffix}"
end
+
+ def options_for_list(custom_field, project)
+ base_options = []
+ unless custom_field.required?
+ unset_label = custom_field.field_format == "user" ? :label_nobody : :label_none
+ base_options << [I18n.t(unset_label), "none"]
+ end
+
+ possible_values = custom_field.possible_values_options(project)
+ options = if custom_field.version?
+ grouped_options_for_select(possible_values.group_by(&:last).to_a)
+ else
+ options_for_select(possible_values)
+ end
+
+ options_for_select(base_options) + options
+ end
end
From c7d76b6f736ed5b96ddfc637d3c3fb73f972a8c4 Mon Sep 17 00:00:00 2001
From: Dombi Attila <83396+dombesz@users.noreply.github.com>
Date: Tue, 25 Feb 2025 15:42:37 +0200
Subject: [PATCH 13/16] Change the primerized version autocompleter to handle
grouped options
---
.../inputs/multi_version_select_list.rb | 1 +
.../inputs/single_version_select_list.rb | 4 +++-
.../custom_fields/inputs/version_select.rb | 18 ++++++++----------
.../forms/dsl/autocompleter_input.rb | 6 ++++--
4 files changed, 16 insertions(+), 13 deletions(-)
diff --git a/app/forms/custom_fields/inputs/multi_version_select_list.rb b/app/forms/custom_fields/inputs/multi_version_select_list.rb
index 9d6c65f3f91f..2173bdaa8bdd 100644
--- a/app/forms/custom_fields/inputs/multi_version_select_list.rb
+++ b/app/forms/custom_fields/inputs/multi_version_select_list.rb
@@ -48,6 +48,7 @@ class CustomFields::Inputs::MultiVersionSelectList < CustomFields::Inputs::Base:
list.option(
label: version.name,
value: version.id,
+ group_by: version.project.name,
selected: selected?(version)
)
end
diff --git a/app/forms/custom_fields/inputs/single_version_select_list.rb b/app/forms/custom_fields/inputs/single_version_select_list.rb
index d2649566f834..0d534a7107f9 100644
--- a/app/forms/custom_fields/inputs/single_version_select_list.rb
+++ b/app/forms/custom_fields/inputs/single_version_select_list.rb
@@ -41,7 +41,9 @@ class CustomFields::Inputs::SingleVersionSelectList < CustomFields::Inputs::Base
custom_value_form.autocompleter(**version_input_attributes) do |list|
assignable_custom_field_values(@custom_field).each do |version|
list.option(
- label: version.name, value: version.id,
+ label: version.name,
+ value: version.id,
+ group_by: version.project.name,
selected: selected?(version)
)
end
diff --git a/app/forms/custom_fields/inputs/version_select.rb b/app/forms/custom_fields/inputs/version_select.rb
index 8ae754b7b1f2..1ea18a46e746 100644
--- a/app/forms/custom_fields/inputs/version_select.rb
+++ b/app/forms/custom_fields/inputs/version_select.rb
@@ -38,23 +38,21 @@ def version_input_attributes
end
def additional_attributes
+ autocomplete_options = { groupBy: "group_by" }
+
if @object.blank? || (@object.respond_to?(:project) && @object.project.blank?)
- {
- autocomplete_options: {
- disabled: true,
- placeholder: I18n.t("custom_fields.placeholder_version_select")
- }
- }
- else
- {}
+ autocomplete_options[:disabled] = true
+ autocomplete_options[:placeholder] = I18n.t("custom_fields.placeholder_version_select")
end
+
+ { autocomplete_options: }
end
def assignable_versions(only_open:)
if @object.is_a?(Project)
- @object.assignable_versions(only_open: only_open)
+ @object.assignable_versions(only_open:)
elsif @object.respond_to?(:project) && @object.project.present?
- @object.project.assignable_versions(only_open: only_open)
+ @object.project.assignable_versions(only_open:)
else
Version.none
end
diff --git a/lib/primer/open_project/forms/dsl/autocompleter_input.rb b/lib/primer/open_project/forms/dsl/autocompleter_input.rb
index 05478085c74e..62ffa20a8541 100644
--- a/lib/primer/open_project/forms/dsl/autocompleter_input.rb
+++ b/lib/primer/open_project/forms/dsl/autocompleter_input.rb
@@ -8,19 +8,21 @@ class AutocompleterInput < Primer::Forms::Dsl::Input
attr_reader :name, :label, :autocomplete_options, :select_options, :wrapper_data_attributes
class Option
- attr_reader :label, :value, :selected, :classes
+ attr_reader :label, :value, :selected, :classes, :group_by
- def initialize(label:, value:, classes: nil, selected: false)
+ def initialize(label:, value:, classes: nil, selected: false, group_by: nil)
@label = label
@value = value
@selected = selected
@classes = classes
+ @group_by = group_by
end
def to_h
{
id: value,
name: label,
+ group_by:,
classes:
}.compact
end
From 2f78237baaf21bb6444d3c5cd6c539798b367c3c Mon Sep 17 00:00:00 2001
From: Dombi Attila <83396+dombesz@users.noreply.github.com>
Date: Tue, 25 Feb 2025 16:46:19 +0200
Subject: [PATCH 14/16] Fix primer autocompleter missing bindValue bug, hide
already selected values.
---
.../open_project/forms/autocompleter.rb | 39 +++++++++++++------
1 file changed, 28 insertions(+), 11 deletions(-)
diff --git a/lib/primer/open_project/forms/autocompleter.rb b/lib/primer/open_project/forms/autocompleter.rb
index 0eab3cfe3304..5e73a02a986a 100644
--- a/lib/primer/open_project/forms/autocompleter.rb
+++ b/lib/primer/open_project/forms/autocompleter.rb
@@ -20,25 +20,42 @@ def initialize(input:, autocomplete_options:, wrapper_data_attributes: {})
@wrapper_data_attributes = wrapper_data_attributes
end
- def extend_autocomplete_inputs(inputs) # rubocop:disable Metrics/AbcSize,Metrics/PerceivedComplexity
- inputs[:classes] = "ng-select--primerized #{@input.invalid? ? '-error' : ''}"
- inputs[:inputName] ||= builder.field_name(@input.name)
- inputs[:labelForId] ||= builder.field_id(@input.name)
- inputs[:defaultData] = true unless inputs.key?(:defaultData)
+ def extend_autocomplete_inputs(inputs)
+ inputs = autocomplete_input_defaults(inputs)
if inputs.delete(:decorated)
- inputs[:items] = @input.select_options.map(&:to_h)
- selected = @input.select_options.filter_map { |option| option.to_h if option.selected }
- inputs[:model] = inputs[:multiple] ? selected : selected.first
- inputs[:defaultData] = false
- inputs[:additionalClassProperty] = "classes"
- inputs[:bindLabel] = "name"
+ inputs = autocomplete_input_decorated(inputs)
elsif builder.object
inputs[:inputValue] ||= builder.object.send(@input.name)
end
inputs
end
+
+ private
+
+ def autocomplete_input_defaults(inputs)
+ inputs[:classes] = "ng-select--primerized #{@input.invalid? ? '-error' : ''}"
+ inputs[:inputName] ||= builder.field_name(@input.name)
+ inputs[:labelForId] ||= builder.field_id(@input.name)
+ inputs[:defaultData] = true unless inputs.key?(:defaultData)
+ inputs[:hideSelected] = true
+ inputs
+ end
+
+ def autocomplete_input_decorated(inputs)
+ selected = @input.select_options.filter_map { |option| option.to_h if option.selected }
+ model = inputs[:multiple] ? selected : selected.first
+
+ inputs.merge(
+ items: @input.select_options.map(&:to_h),
+ model:,
+ defaultData: false,
+ additionalClassProperty: "classes",
+ bindLabel: "name",
+ bindValue: "id"
+ )
+ end
end
end
end
From 7fe2c3ebb966a32423739eeb94043d3b341e39bf Mon Sep 17 00:00:00 2001
From: Dombi Attila <83396+dombesz@users.noreply.github.com>
Date: Tue, 25 Feb 2025 17:28:22 +0200
Subject: [PATCH 15/16] Fix spec
---
app/models/queries/filters/strategies/list.rb | 11 +++++------
.../custom_actions/actions/custom_field_spec.rb | 2 +-
2 files changed, 6 insertions(+), 7 deletions(-)
diff --git a/app/models/queries/filters/strategies/list.rb b/app/models/queries/filters/strategies/list.rb
index 0a4eaf4d76ce..01af460e5645 100644
--- a/app/models/queries/filters/strategies/list.rb
+++ b/app/models/queries/filters/strategies/list.rb
@@ -30,6 +30,9 @@
module Queries::Filters::Strategies
class List < BaseStrategy
+ delegate :allowed_values,
+ to: :filter
+
self.supported_operators = ["=", "!"]
self.default_operator = "="
@@ -50,16 +53,12 @@ def validate
end
end
- def allowed_values
- filter.allowed_values.map { |_, v| v.to_s }
- end
-
def valid_values!
- filter.values &= (allowed_values + ["-1"])
+ filter.values &= (allowed_values.map { |_, v| v.to_s } + ["-1"])
end
def non_valid_values?
- (values.compact_blank & (allowed_values + ["-1"])) != values.compact_blank
+ (values.compact_blank & (allowed_values.map { |_, v| v.to_s } + ["-1"])) != values.compact_blank
end
end
end
diff --git a/spec/models/custom_actions/actions/custom_field_spec.rb b/spec/models/custom_actions/actions/custom_field_spec.rb
index 644856e59880..d1b4d7ce2f7c 100644
--- a/spec/models/custom_actions/actions/custom_field_spec.rb
+++ b/spec/models/custom_actions/actions/custom_field_spec.rb
@@ -112,7 +112,7 @@
expect(described_class.all.map(&:custom_field))
.to match_array(custom_fields)
- described_class.find_each do |subclass|
+ described_class.all.each do |subclass|
expect(subclass.ancestors).to include(described_class)
end
end
From 2fa1c8fca55e131742837d4d125a4975d8f3c688 Mon Sep 17 00:00:00 2001
From: Dombi Attila <83396+dombesz@users.noreply.github.com>
Date: Tue, 25 Feb 2025 20:33:45 +0200
Subject: [PATCH 16/16] Revert the hideSelected option on the primer
autocompleter.
---
lib/primer/open_project/forms/autocompleter.rb | 1 -
1 file changed, 1 deletion(-)
diff --git a/lib/primer/open_project/forms/autocompleter.rb b/lib/primer/open_project/forms/autocompleter.rb
index 5e73a02a986a..43440e24ed70 100644
--- a/lib/primer/open_project/forms/autocompleter.rb
+++ b/lib/primer/open_project/forms/autocompleter.rb
@@ -39,7 +39,6 @@ def autocomplete_input_defaults(inputs)
inputs[:inputName] ||= builder.field_name(@input.name)
inputs[:labelForId] ||= builder.field_id(@input.name)
inputs[:defaultData] = true unless inputs.key?(:defaultData)
- inputs[:hideSelected] = true
inputs
end