Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Do not focus custom fields in the create child dialog #17636

Merged
merged 3 commits into from
Jan 27, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 2 additions & 6 deletions app/forms/custom_fields/custom_field_rendering.rb
Original file line number Diff line number Diff line change
Expand Up @@ -84,9 +84,7 @@ def single_value_custom_field_input(builder, custom_field)
CustomFields::Inputs::Int.new(builder, **form_args)
when "float"
CustomFields::Inputs::Float.new(builder, **form_args)
when "hierarchy"
CustomFields::Inputs::SingleSelectList.new(builder, **form_args)
when "list"
when "hierarchy", "list"
CustomFields::Inputs::SingleSelectList.new(builder, **form_args)
when "date"
CustomFields::Inputs::Date.new(builder, **form_args)
Expand All @@ -103,9 +101,7 @@ def multi_value_custom_field_input(builder, custom_field)
form_args = form_arguments(custom_field)

case custom_field.field_format
when "hierarchy"
CustomFields::Inputs::MultiSelectList.new(builder, **form_args)
when "list"
when "hierarchy", "list"
CustomFields::Inputs::MultiSelectList.new(builder, **form_args)
when "user"
CustomFields::Inputs::MultiUserSelectList.new(builder, **form_args)
Expand Down
2 changes: 0 additions & 2 deletions app/helpers/custom_fields_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -205,8 +205,6 @@ def format_value(value, custom_field)
# Return an array of custom field formats which can be used in select_tag
def custom_field_formats_for_select(custom_field)
OpenProject::CustomFieldFormat.all_for_field(custom_field)
.sort_by(&:order)
.reject { |format| format.label.nil? }
.map do |custom_field_format|
[label_for_custom_field_format(custom_field_format.name), custom_field_format.name]
end
Expand Down
2 changes: 1 addition & 1 deletion app/models/custom_field.rb
Original file line number Diff line number Diff line change
Expand Up @@ -296,7 +296,7 @@ def field_format_hierarchy?
end

def multi_value_possible?
version? || user? || list? || field_format_hierarchy?
OpenProject::CustomFieldFormat.find_by(name: field_format)&.multi_value_possible?
end

def allow_non_open_versions_possible?
Expand Down
4 changes: 4 additions & 0 deletions config/initializers/custom_field_format.rb
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
fields.register OpenProject::CustomFieldFormat.new("list",
label: :label_list,
order: 6,
multi_value_possible: true,
formatter: "CustomValue::ListStrategy")
fields.register OpenProject::CustomFieldFormat.new("date",
label: :label_date,
Expand All @@ -64,12 +65,14 @@
only: %w(WorkPackage TimeEntry Version Project),
edit_as: "list",
order: 9,
multi_value_possible: true,
formatter: "CustomValue::UserStrategy")
fields.register OpenProject::CustomFieldFormat.new("version",
label: Proc.new { Version.model_name.human },
only: %w(WorkPackage TimeEntry Version Project),
edit_as: "list",
order: 10,
multi_value_possible: true,
formatter: "CustomValue::VersionStrategy")
# This is an internal formatter used as a fallback in case a value is not found.
# Setting the label to nil in order to avoid it becoming available for selection as a custom value format.
Expand All @@ -82,5 +85,6 @@
label: :label_hierarchy,
only: %w(WorkPackage),
order: 12,
multi_value_possible: true,
formatter: "CustomValue::HierarchyStrategy")
end
36 changes: 26 additions & 10 deletions lib/open_project/custom_field_format.rb
Original file line number Diff line number Diff line change
Expand Up @@ -30,18 +30,29 @@ module OpenProject
class CustomFieldFormat
include Redmine::I18n

cattr_accessor :available
cattr_reader :available
@@available = {}

attr_accessor :name, :order, :label, :edit_as, :class_names, :formatter
attr_reader :name, :order, :label, :edit_as, :class_names

def initialize(name, label:, order:, edit_as: name, only: nil, formatter: "CustomValue::StringStrategy")
self.name = name
self.label = label
self.order = order
self.edit_as = edit_as
self.class_names = only
self.formatter = formatter
def initialize(name,
label:,
order:,
edit_as: name,
only: nil,
multi_value_possible: false,
formatter: "CustomValue::StringStrategy")
@name = name
@label = label
@order = order
@edit_as = edit_as
@class_names = only
@multi_value_possible = multi_value_possible
@formatter = formatter
end

def multi_value_possible?
@multi_value_possible
end

def formatter
Expand All @@ -56,7 +67,7 @@ def map(&)

# Registers a custom field format
def register(custom_field_format, _options = {})
@@available[custom_field_format.name] = custom_field_format unless @@available.keys.include?(custom_field_format.name)
@@available[custom_field_format.name] = custom_field_format unless @@available.include?(custom_field_format.name)
end

def available_formats
Expand All @@ -69,10 +80,15 @@ def find_by(name:)

def all_for_field(custom_field)
class_name = custom_field.class.customized_class.name
all_for_class_name(class_name)
end

def all_for_class_name(class_name)
available
.values
.select { |field| field.class_names.nil? || field.class_names.include?(class_name) }
.sort_by(&:order)
.reject { |format| format.label.nil? }
end
end
end
Expand Down
9 changes: 9 additions & 0 deletions spec/factories/custom_field_factory.rb
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,15 @@

trait :hierarchy do
field_format { "hierarchy" }
hierarchy_root do
service = CustomFields::Hierarchy::HierarchicalItemService.new
service.generate_root(instance).value!
end
end

trait :multi_hierarchy do
hierarchy
multi_value
end

factory :project_custom_field, class: "ProjectCustomField" do
Expand Down
39 changes: 39 additions & 0 deletions spec/features/work_packages/tabs/relations_children_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
require "support/edit_fields/edit_field"

RSpec.describe "Relations children tab", :js, :with_cuprite do
include CustomFieldsHelpers

shared_let(:normal_cf) { create(:string_wp_custom_field, is_required: false) }
shared_let(:required_cf) { create(:string_wp_custom_field, is_required: true) }
shared_let(:type_milestone) { create(:type_milestone) }
Expand Down Expand Up @@ -113,4 +115,41 @@
relations_tab.expect_no_new_relation_type("New child")
end
end

context "when all possible custom fields are there" do
let!(:user) { create(:admin) }

before do
factory_bot_custom_field_traits_for("WorkPackage")
.product([true, false])
.each do |trait, is_required|
cf = create(:wp_custom_field, trait, is_required:)
project.types.first.custom_fields << cf
project.work_package_custom_fields << cf
end
end

it "displays a field for each required custom field" do
wp_page.visit_tab!("relations")
relations_tab.select_relation_type "New child"

project.work_package_custom_fields.each do |cf|
create_dialog.in_dialog do
if cf.required?
# `visible: :all` is needed as text custom field use a hidden textarea internally
expect(page).to have_field cf.name, visible: :all
else
expect(page).to have_no_field cf.name
end
end
end
end

it "focuses the subject input field" do
wp_page.visit_tab!("relations")
relations_tab.select_relation_type "New child"

create_dialog.expect_subject_field_focused
end
end
end
2 changes: 1 addition & 1 deletion spec/lib/journal_formatter/custom_field_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -401,7 +401,7 @@ def url_helper = Rails.application.routes.url_helpers
context "for hierarchy custom field" do
shared_let(:custom_field) { create(:hierarchy_wp_custom_field) }
shared_let(:service) { CustomFields::Hierarchy::HierarchicalItemService.new }
shared_let(:root) { service.generate_root(custom_field).value! }
shared_let(:root) { custom_field.hierarchy_root }
shared_let(:luke) { service.insert_item(parent: root, label: "luke", short: "LS").value! }
shared_let(:mara) { service.insert_item(parent: luke, label: "mara").value! }

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
require Rails.root.join("db/migrate/20250102161733_adds_position_cache_to_hierarchy_items.rb")

RSpec.describe AddsPositionCacheToHierarchyItems, type: :model do
let(:custom_field) { create(:hierarchy_wp_custom_field) }
let(:custom_field) { create(:hierarchy_wp_custom_field, hierarchy_root: nil) }
let(:service) { CustomFields::Hierarchy::HierarchicalItemService.new }

it "backfills the position_cache value on already existing hierarchy items" do
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
# Roll back the migration so we can migrate up
before do
ActiveRecord::Migration.suppress_messages { described_class.new.migrate(:down) }
CustomField.reset_column_information
end

# Silencing migration logs, since we are not interested in that during testing
Expand Down
2 changes: 1 addition & 1 deletion spec/models/custom_field/order_statements_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
# integration tests at spec/models/query/results_cf_sorting_integration_spec.rb
context "when hierarchy" do
let(:service) { CustomFields::Hierarchy::HierarchicalItemService.new }
let(:item) { service.generate_root(custom_field).value! }
let(:item) { custom_field.hierarchy_root }

subject(:custom_field) { create(:hierarchy_wp_custom_field) }

Expand Down
8 changes: 8 additions & 0 deletions spec/support/components/work_packages/create_dialog.rb
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,14 @@ def expect_subject(value)
end
end

def expect_subject_field_focused
in_dialog do
subject_field = page.find_field("Subject")
subject_field_id_selector = "##{subject_field[:id]}"
expect(page).to have_focus_on(subject_field_id_selector)
end
end

def set_description(value)
@description.set_markdown(value)
end
Expand Down
51 changes: 51 additions & 0 deletions spec/support/custom_fields_helpers.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# frozen_string_literal: true

#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) the OpenProject GmbH
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See COPYRIGHT and LICENSE files for more details.
#++

module CustomFieldsHelpers
def factory_bot_custom_field_traits_for(class_name)
OpenProject::CustomFieldFormat
.all_for_class_name(class_name)
.flat_map do |format|
trait_name = trait_name(format.name)
[
trait_name,
format.multi_value_possible? ? "multi_#{trait_name}" : nil
].compact
end
end

def trait_name(custom_field_format_name)
case custom_field_format_name
when "int" then "integer"
when "bool" then "boolean"
else custom_field_format_name
end
end
end
Loading