diff --git a/app/views/admin/settings/authentication_settings/_passwords.html.erb b/app/views/admin/settings/authentication_settings/_passwords.html.erb index 7203c23b17f..3179782dfe5 100644 --- a/app/views/admin/settings/authentication_settings/_passwords.html.erb +++ b/app/views/admin/settings/authentication_settings/_passwords.html.erb @@ -56,9 +56,9 @@ See COPYRIGHT and LICENSE files for more details. input_width: :small - form.hidden(name: "settings[password_active_rules][]", value: "", scope_name_to_model: false) form.check_box_group(name: :password_active_rules, disabled:, + include_hidden: true, label: I18n.t(:setting_password_active_rules)) do |group| OpenProject::Passwords::Evaluator.known_rules.each do |value| group.check_box(value:, diff --git a/lib/primer/open_project/forms/dsl/input_methods.rb b/lib/primer/open_project/forms/dsl/input_methods.rb index 1ed63ce8168..1ecdb76798b 100644 --- a/lib/primer/open_project/forms/dsl/input_methods.rb +++ b/lib/primer/open_project/forms/dsl/input_methods.rb @@ -19,7 +19,8 @@ module Primer super(**decorate_options(**), &) end - def check_box_group(**, &) + def check_box_group(include_hidden: false, **, &) + add_input Primer::Forms::Dsl::HiddenInput.new(builder:, form:, multiple: true, value: "", **) if include_hidden super(**decorate_options(**), &) end diff --git a/lookbook/previews/open_project/common/advanced_form_inputs_preview.rb b/lookbook/previews/open_project/common/advanced_form_inputs_preview.rb index 1df46613f3c..5797a60f267 100644 --- a/lookbook/previews/open_project/common/advanced_form_inputs_preview.rb +++ b/lookbook/previews/open_project/common/advanced_form_inputs_preview.rb @@ -39,6 +39,22 @@ module OpenProject def advanced_check_box_group render_with_template end + + # `check_box_group` with `include_hidden: true` + # + # HTML forms do not submit unchecked checkboxes, so when every option in an + # array-valued `check_box_group` is unchecked, the field key is absent from + # the request params entirely. The server then has no way to distinguish + # "user cleared all selections" from "this field was not part of the form", + # and the previous value is silently preserved. + # + # Pass `include_hidden: true` to emit a hidden sentinel field before the + # checkboxes. This mirrors the behaviour of Rails' own `check_box` helper + # and ensures the key is always present in params — as an empty array when + # nothing is checked — so the server can save the empty selection correctly. + def check_box_group_with_include_hidden + render_with_template + end end end end diff --git a/lookbook/previews/open_project/common/advanced_form_inputs_preview/check_box_group_with_include_hidden.html.erb b/lookbook/previews/open_project/common/advanced_form_inputs_preview/check_box_group_with_include_hidden.html.erb new file mode 100644 index 00000000000..8b7524c9534 --- /dev/null +++ b/lookbook/previews/open_project/common/advanced_form_inputs_preview/check_box_group_with_include_hidden.html.erb @@ -0,0 +1,22 @@ +<% + the_form = Class.new(ApplicationForm) do + form do |f| + f.check_box_group(name: :colors, include_hidden: true, label: "Favorite colors") do |group| + group.check_box(value: "red", label: "Red") + group.check_box(value: "green", label: "Green") + group.check_box(value: "blue", label: "Blue") + end + + f.submit(name: "submit", label: "Save") + end + end +%> + +<%= + primer_form_with( + url: "/abc", + method: :post + ) do |f| + render(the_form.new(f)) + end +%>