mirror of
https://github.com/opf/openproject.git
synced 2026-06-13 19:20:00 +00:00
Get inplaceEditField for customField dynamically by the format instead of registring them all directly
This commit is contained in:
@@ -60,7 +60,11 @@ module OpenProject
|
||||
end
|
||||
|
||||
def field_class
|
||||
OpenProject::InplaceEdit::FieldRegistry.fetch(attribute)
|
||||
if custom_field?
|
||||
OpenProject::InplaceEdit::FieldRegistry.fetch_for_custom_field_format(custom_field&.field_format)
|
||||
else
|
||||
OpenProject::InplaceEdit::FieldRegistry.fetch(attribute)
|
||||
end
|
||||
end
|
||||
|
||||
def edit_field_component(form)
|
||||
@@ -212,8 +216,7 @@ module OpenProject
|
||||
# For custom fields, check the is_required attribute
|
||||
custom_field&.is_required || false
|
||||
else
|
||||
# For regular model attributes, check ActiveRecord validations
|
||||
model.class.validators_on(attribute).any?(ActiveRecord::Validations::PresenceValidator)
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
+4
@@ -38,10 +38,14 @@ module OpenProject
|
||||
|
||||
attr_reader :model, :attribute
|
||||
|
||||
# If the writable attribute is not explicitly listed as an argument,
|
||||
# it will be interpreted as one of the system_arguments and thus overwrite the `writable: false`
|
||||
# rubocop:disable Lint/UnusedMethodArgument
|
||||
def initialize(model:, attribute:, writable: nil, truncated: false, has_comment: false, show_comment: false,
|
||||
**system_arguments)
|
||||
super(model:, attribute:, writable: false, truncated:, has_comment:, show_comment:, **system_arguments)
|
||||
end
|
||||
# rubocop:enable Lint/UnusedMethodArgument
|
||||
|
||||
def render_calculation_error
|
||||
error = custom_field&.first_calculation_error(model)
|
||||
|
||||
+1
@@ -3,6 +3,7 @@
|
||||
padding: var(--base-size-4) var(--base-size-8)
|
||||
border: 1px solid transparent
|
||||
border-radius: var(--borderRadius-medium)
|
||||
@include text-shortener(false)
|
||||
|
||||
&:hover, &:focus
|
||||
border-color: var(--borderColor-default)
|
||||
|
||||
+1
-2
@@ -51,8 +51,7 @@ module OpenProject
|
||||
link = Addressable::URI.parse(href)
|
||||
return href unless link
|
||||
|
||||
target = link.host == Setting.host_without_protocol ? "_top" : "_blank"
|
||||
render(Primer::Beta::Link.new(href:, rel: "noopener noreferrer", target:)) do
|
||||
render(Primer::Beta::Link.new(href:, rel: "noopener noreferrer")) do
|
||||
href
|
||||
end
|
||||
end
|
||||
|
||||
@@ -46,27 +46,8 @@ class InplaceEditFieldsController < ApplicationController
|
||||
|
||||
def update
|
||||
success = invoke_update_handler
|
||||
|
||||
if success
|
||||
render_success_flash_message_via_turbo_stream(
|
||||
message: I18n.t(:notice_successful_update)
|
||||
)
|
||||
close_dialog_via_turbo_stream(dialog_id) if dialog_id
|
||||
refresh_calculated_dependents
|
||||
end
|
||||
|
||||
if !success && dialog_id
|
||||
replace_via_turbo_stream(
|
||||
component: dialog_field_component,
|
||||
status: :unprocessable_entity
|
||||
)
|
||||
else
|
||||
replace_via_turbo_stream(
|
||||
component: component(enforce_edit_mode: !success),
|
||||
status: success ? :ok : :unprocessable_entity
|
||||
)
|
||||
end
|
||||
|
||||
handle_update_success if success
|
||||
replace_field_component(success)
|
||||
respond_with_turbo_streams
|
||||
rescue ArgumentError
|
||||
head :not_found
|
||||
@@ -96,6 +77,28 @@ class InplaceEditFieldsController < ApplicationController
|
||||
handler.call(model: @model, params: permitted_params, user: current_user)
|
||||
end
|
||||
|
||||
def handle_update_success
|
||||
render_success_flash_message_via_turbo_stream(
|
||||
message: I18n.t(:notice_successful_update)
|
||||
)
|
||||
close_dialog_via_turbo_stream(dialog_id) if dialog_id
|
||||
refresh_calculated_dependents
|
||||
end
|
||||
|
||||
def replace_field_component(success)
|
||||
if !success && dialog_id
|
||||
replace_via_turbo_stream(
|
||||
component: dialog_field_component,
|
||||
status: :unprocessable_entity
|
||||
)
|
||||
else
|
||||
replace_via_turbo_stream(
|
||||
component: component(enforce_edit_mode: !success),
|
||||
status: success ? :ok : :unprocessable_entity
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
def find_model
|
||||
model_class = resolve_model_class(params[:model])
|
||||
@model = model_class.visible.find(params[:id])
|
||||
|
||||
@@ -88,7 +88,7 @@ class CustomField < ApplicationRecord
|
||||
validates :has_comment, absence: true, unless: :can_have_comment?
|
||||
|
||||
before_validation :check_searchability
|
||||
after_create :register_inplace_edit_component
|
||||
|
||||
after_destroy :destroy_help_text
|
||||
|
||||
# make sure int, float, date, and bool are not searchable
|
||||
@@ -482,8 +482,4 @@ class CustomField < ApplicationRecord
|
||||
.where(attribute_name:)
|
||||
.destroy_all
|
||||
end
|
||||
|
||||
def register_inplace_edit_component
|
||||
OpenProject::InplaceEdit::FieldRegistry.register_custom_field(id, field_format)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -53,12 +53,6 @@ Rails.application.config.to_prepare do
|
||||
|
||||
OpenProject::InplaceEdit::FieldRegistry.register_custom_field_format_mappings(custom_field_format_mappings)
|
||||
|
||||
if CustomField.table_exists?
|
||||
CustomField.pluck(:id, :field_format).each do |id, field_format|
|
||||
OpenProject::InplaceEdit::FieldRegistry.register_custom_field(id, field_format)
|
||||
end
|
||||
end
|
||||
|
||||
# Register the update handler per model
|
||||
OpenProject::InplaceEdit::UpdateRegistry.register(Project,
|
||||
handler: OpenProject::InplaceEdit::Handlers::ProjectUpdate,
|
||||
|
||||
@@ -44,21 +44,22 @@ module OpenProject
|
||||
@custom_field_format_mappings = mappings
|
||||
end
|
||||
|
||||
def register_custom_field(id, field_format)
|
||||
component = @custom_field_format_mappings[field_format]
|
||||
register("custom_field_#{id}", component) if component
|
||||
end
|
||||
|
||||
def fetch(attribute_name)
|
||||
@registry.fetch(attribute_name.to_s) { Common::InplaceEditFields::TextInputComponent }
|
||||
end
|
||||
|
||||
def fetch_for_custom_field_format(field_format)
|
||||
return Common::InplaceEditFields::TextInputComponent if field_format.nil?
|
||||
|
||||
@custom_field_format_mappings.fetch(field_format.to_s) { Common::InplaceEditFields::TextInputComponent }
|
||||
end
|
||||
|
||||
@default = new
|
||||
|
||||
class << self
|
||||
attr_reader :default
|
||||
|
||||
delegate :register, :fetch, :register_custom_field_format_mappings, :register_custom_field, to: :@default
|
||||
delegate :register, :fetch, :fetch_for_custom_field_format, :register_custom_field_format_mappings, to: :@default
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
+5
-1
@@ -46,7 +46,11 @@ RSpec.describe "Edit project custom fields on project overview page", "attribute
|
||||
context "without attribute help texts defined" do
|
||||
it "shows field labels without help text link" do
|
||||
input_fields.each do |custom_field|
|
||||
field = overview_page.open_inplace_edit_field_for_custom_field(custom_field)
|
||||
field = if custom_field == text_project_custom_field
|
||||
overview_page.open_modal_for_custom_field(custom_field)
|
||||
else
|
||||
overview_page.open_inplace_edit_field_for_custom_field(custom_field)
|
||||
end
|
||||
field.expect_field_label_without_help_text custom_field.name
|
||||
field.close
|
||||
end
|
||||
|
||||
@@ -63,33 +63,32 @@ RSpec.describe OpenProject::InplaceEdit::FieldRegistry do
|
||||
end
|
||||
|
||||
describe "#register_custom_field_format_mappings" do
|
||||
it "stores format-to-component mappings used by register_custom_field" do
|
||||
it "stores format-to-component mappings used by fetch_for_custom_field_format" do
|
||||
text_component = Class.new
|
||||
registry.register_custom_field_format_mappings("text" => text_component)
|
||||
|
||||
registry.register_custom_field(42, "text")
|
||||
|
||||
expect(registry.fetch("custom_field_42")).to eq(text_component)
|
||||
expect(registry.fetch_for_custom_field_format("text")).to eq(text_component)
|
||||
end
|
||||
end
|
||||
|
||||
describe "#register_custom_field" do
|
||||
describe "#fetch_for_custom_field_format" do
|
||||
let(:text_component) { Class.new }
|
||||
|
||||
before do
|
||||
registry.register_custom_field_format_mappings("text" => text_component)
|
||||
end
|
||||
|
||||
it "registers the correct component for the given field format" do
|
||||
registry.register_custom_field(1, "text")
|
||||
|
||||
expect(registry.fetch("custom_field_1")).to eq(text_component)
|
||||
it "returns the correct component for the given field format" do
|
||||
expect(registry.fetch_for_custom_field_format("text")).to eq(text_component)
|
||||
end
|
||||
|
||||
it "does nothing when the format has no mapping" do
|
||||
registry.register_custom_field(2, "unknown_format")
|
||||
it "falls back to TextInputComponent when the format has no mapping" do
|
||||
expect(registry.fetch_for_custom_field_format("unknown_format"))
|
||||
.to eq(OpenProject::Common::InplaceEditFields::TextInputComponent)
|
||||
end
|
||||
|
||||
expect(registry.fetch("custom_field_2"))
|
||||
it "falls back to TextInputComponent when field_format is nil" do
|
||||
expect(registry.fetch_for_custom_field_format(nil))
|
||||
.to eq(OpenProject::Common::InplaceEditFields::TextInputComponent)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -742,28 +742,4 @@ RSpec.describe CustomField do
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "after_create callback" do
|
||||
it "registers the custom field in the inplace edit field registry" do
|
||||
custom_field = build(:custom_field, field_format: "string")
|
||||
|
||||
allow(OpenProject::InplaceEdit::FieldRegistry).to receive(:register_custom_field)
|
||||
|
||||
custom_field.save!
|
||||
|
||||
expect(OpenProject::InplaceEdit::FieldRegistry)
|
||||
.to have_received(:register_custom_field)
|
||||
.with(anything, "string")
|
||||
end
|
||||
|
||||
it "does not re-register when updated" do
|
||||
custom_field = create(:custom_field, field_format: "string")
|
||||
|
||||
allow(OpenProject::InplaceEdit::FieldRegistry).to receive(:register_custom_field)
|
||||
|
||||
custom_field.update!(name: "Updated name")
|
||||
|
||||
expect(OpenProject::InplaceEdit::FieldRegistry).not_to have_received(:register_custom_field)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user