-
Notifications
You must be signed in to change notification settings - Fork 3.1k
Feature/70292 primerize custom field forms #21567
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
base: dev
Are you sure you want to change the base?
Conversation
|
|
||
| if (cb.checked) { | ||
| const multi = this.multiSelectTargets[0] as HTMLInputElement|undefined; | ||
| const multi = undefined; // FIXME this.multiSelectTargets[0] as HTMLInputElement|undefined; |
Check notice
Code scanning / CodeQL
Unused variable, import, function or class Note
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI 21 days ago
In general, unused variables should be removed when they are not read anywhere, especially if any logic depending on them is already commented out. If future logic is planned, that intent can be captured in comments or TODO/FIXME notes without introducing dead code.
Here, the multi variable on line 153 is never used. The logic that would have referenced it is commented out. To fix this without changing behavior, delete the const multi = ... line, but keep the FIXME comment so the future plan remains documented. No imports or additional methods are needed. Only the uncheckOtherDefaults method in frontend/src/stimulus/controllers/dynamic/admin/custom-fields.controller.ts needs editing, removing that single declaration line.
-
Copy modified line R153
| @@ -150,8 +150,7 @@ | ||
| const cb = event.target as HTMLInputElement; | ||
|
|
||
| if (cb.checked) { | ||
| const multi = undefined; // FIXME this.multiSelectTargets[0] as HTMLInputElement|undefined; | ||
|
|
||
| // FIXME use this.multiSelectTargets[0] as HTMLInputElement|undefined | ||
| // if (multi?.checked === false) { | ||
| // this.customOptionDefaultsTargets.forEach((el) => (el.checked = false)); | ||
| // cb.checked = true; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This PR refactors the custom field forms to use Primer Design System components (ViewComponent-based) instead of the legacy form helpers. The changes replace the old ERB-based forms with modular form components organized under CustomFields::Details::* and introduce new component-based rendering for custom options tables.
Changes:
- Replaced legacy form helpers with Primer Forms components for custom field configuration
- Refactored custom options table into reusable ViewComponent-based components
- Removed
CustomFieldFormatDependent#attributesmethod in favor of simplervisible?method - Updated Stimulus controller to use template-based row cloning instead of manual DOM manipulation
Reviewed changes
Copilot reviewed 37 out of 37 changed files in this pull request and generated 12 comments.
Show a summary per file
| File | Description |
|---|---|
spec/lib/open_project/custom_field_format_dependent_spec.rb |
Removed obsolete spec testing the removed attributes and stimulus_config methods |
spec/components/custom_fields/custom_options_component_spec.rb |
Added component test for the new CustomOptionsComponent |
spec/components/custom_fields/custom_options/table_component_spec.rb |
Added component test for the custom options table |
spec/components/custom_fields/custom_options/row_component_spec.rb |
Added component test for custom option rows |
lib/open_project/custom_field_format_dependent.rb |
Simplified to remove format-dependent targets config and expose only visible? method |
frontend/src/stimulus/controllers/dynamic/admin/custom-fields.controller.ts |
Updated to use template-based row addition and removed format-dependent target management |
app/views/custom_fields/new.html.erb |
Converted from labelled_tabular_form_for to settings_primer_form_with |
app/views/custom_fields/edit.html.erb |
Converted from labelled_tabular_form_for to settings_primer_form_with |
app/views/custom_fields/_form.html.erb |
Replaced inline form fields with modular Primer Form components |
app/views/custom_fields/_custom_options.html.erb |
Removed legacy partial, replaced by component |
app/views/admin/settings/project_custom_fields/new.html.erb |
Converted to settings_primer_form_with |
app/views/admin/settings/project_custom_fields/edit.html.erb |
Converted to settings_primer_form_with |
app/forms/custom_fields/save.rb |
New form component for submit button |
app/forms/custom_fields/details/*.rb |
New modular form components for each custom field detail section |
app/components/table_component.rb |
Enhanced to accept table_arguments for passing attributes to table element |
app/components/table_component.html.erb |
Updated to use Primer::BaseComponent for table rendering |
app/components/custom_fields/custom_options_component.rb |
New component orchestrating custom options display |
app/components/custom_fields/custom_options_component.html.erb |
Template for custom options with table and hidden template row |
app/components/custom_fields/custom_options/table_component.rb |
Table component for custom options |
app/components/custom_fields/custom_options/row_component.rb |
Row component for individual custom option |
app/components/custom_fields/custom_options/base_row_component.rb |
Base class for custom option rows with common functionality |
| f.check_box( | ||
| name: :editable, | ||
| label: attribute_name(:editable), | ||
| caption: instructions_for(:admin_only) |
Copilot
AI
Jan 16, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The caption for the editable checkbox is incorrectly using :admin_only instead of :editable. This will display the wrong help text. Change to instructions_for(:editable).
| caption: instructions_for(:admin_only) | |
| caption: instructions_for(:editable) |
| f.check_box( | ||
| name: :searchable, | ||
| label: attribute_name(:searchable), | ||
| caption: for_project ? instructions_for(:searchable_for_project) : instructions_for(:searchable) |
Copilot
AI
Jan 16, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The translation key :searchable_for_project does not exist in the translations. Based on the en.yml file, the correct keys are :searchable with nested keys all and project. Change to instructions_for(for_project ? :searchable_for_project : :searchable) and add the missing translation key, or use the existing structure like I18n.t(for_project ? 'custom_fields.instructions.searchable.project' : 'custom_fields.instructions.searchable.all').
| caption: for_project ? instructions_for(:searchable_for_project) : instructions_for(:searchable) | |
| caption: I18n.t( | |
| for_project ? 'custom_fields.instructions.searchable.project' : 'custom_fields.instructions.searchable.all' | |
| ) |
| f.check_box( | ||
| name: :is_required, | ||
| label: attribute_name(:is_required), | ||
| caption: for_project ? instructions_for(:is_required_for_project) : instructions_for(:is_required) |
Copilot
AI
Jan 16, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The translation key :is_required_for_project does not exist. Based on en.yml, the correct structure uses nested keys under is_required with all and project sub-keys. Change to use the existing translation structure like I18n.t(for_project ? 'custom_fields.instructions.is_required.project' : 'custom_fields.instructions.is_required.all').
| caption: for_project ? instructions_for(:is_required_for_project) : instructions_for(:is_required) | |
| caption: I18n.t(for_project ? 'custom_fields.instructions.is_required.project' : 'custom_fields.instructions.is_required.all') |
| <% when "ProjectCustomField" %> | ||
| <div class="form--field"> | ||
| <%= f.check_box :is_required %> | ||
| <div class="form--field-instructions"> | ||
| <p><%= t("custom_fields.instructions.is_required.project") %></p> | ||
| </div> | ||
| </div> | ||
| <div class="form--field"> | ||
| <%= f.check_box :is_for_all %> | ||
| <div class="form--field-instructions"> | ||
| <p><%= t("custom_fields.instructions.is_for_all.project") %></p> | ||
| </div> | ||
| </div> | ||
| <div class="form--field"> | ||
| <%= f.check_box :admin_only %> | ||
| <div class="form--field-instructions"> | ||
| <p><%= t("custom_fields.instructions.admin_only.project") %></p> | ||
| </div> | ||
| </div> | ||
| <div class="form--field" <%= format_dependent.attributes(:searchable) %>> | ||
| <%= f.check_box :searchable %> | ||
| <div class="form--field-instructions"> | ||
| <p><%= t("custom_fields.instructions.searchable.project") %></p> | ||
| </div> | ||
| </div> | ||
| <% when "TimeEntryCustomField" %> | ||
| <div class="form--field"> | ||
| <%= f.check_box :is_required %> | ||
| <div class="form--field-instructions"> | ||
| <p><%= t("custom_fields.instructions.is_required.all") %></p> | ||
| </div> | ||
| </div> | ||
| <%= | ||
| render( | ||
| Primer::Forms::FormList.new( | ||
| CustomFields::Details::RequiredForm.new(f, for_project: true), | ||
| CustomFields::Details::AdminOnlyForm.new(f), | ||
| CustomFields::Details::SearchableForm.new(f, for_project: true) | ||
| ) | ||
| ) | ||
| %> |
Copilot
AI
Jan 16, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The is_for_all field is missing for ProjectCustomField. According to the removed old form code, ProjectCustomField should have the following fields: is_required, is_for_all, admin_only, and searchable. The is_for_all field needs to be added. Consider adding a CustomFields::Details::IsForAllForm.new(f) component between RequiredForm and AdminOnlyForm, or including it in the AdminOnlyForm or a ProjectCustomFieldForm component.
| const multi = undefined; // FIXME this.multiSelectTargets[0] as HTMLInputElement|undefined; | ||
|
|
||
| if (multi?.checked === false) { | ||
| this.customOptionDefaultsTargets.forEach((el) => (el.checked = false)); | ||
| cb.checked = true; | ||
| } | ||
| // if (multi?.checked === false) { | ||
| // this.customOptionDefaultsTargets.forEach((el) => (el.checked = false)); | ||
| // cb.checked = true; | ||
| // } |
Copilot
AI
Jan 16, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The uncheckOtherDefaults functionality is commented out with a FIXME note. This breaks the behavior where checking a default value should uncheck others when multi-select is disabled. The multi-select target needs to be properly identified in the new component structure, or this logic needs to be reimplemented to work with the new form components.
| if (idx < this.customOptionRowTargets.length - 1) { | ||
| this.customOptionRowTargets[idx + 1].after(row); | ||
| const idx = this.customOptionRows.indexOf(row); | ||
|
|
Copilot
AI
Jan 16, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Trailing whitespace on an otherwise empty line should be removed for code cleanliness.
| <section | ||
| class="form--section" | ||
| id="custom_field_form"> | ||
| ++#%> |
Copilot
AI
Jan 16, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The ERB closing tag has unusual formatting with +++#%> instead of the standard ++#%>. While this might work, it's inconsistent with the opening tag format and could be confusing.
| ++#%> | |
| %> |
| See COPYRIGHT and LICENSE files for more details. | ||
| ++#%> |
Copilot
AI
Jan 16, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The ERB closing tag has unusual formatting with +++#%> instead of the standard ++#%>. Ensure consistency with the opening copyright comment format.
| ++#%> | |
| --%> |
|
|
||
| event.preventDefault(); | ||
| event.stopImmediatePropagation(); | ||
|
|
Copilot
AI
Jan 16, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Trailing whitespace should be removed from this empty line.
|
|
||
| if (cb.checked) { | ||
| const multi = this.multiSelectTargets[0] as HTMLInputElement|undefined; | ||
| const multi = undefined; // FIXME this.multiSelectTargets[0] as HTMLInputElement|undefined; |
Copilot
AI
Jan 16, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unused variable multi.
Ticket
https://community.openproject.org/wp/70292
What are you trying to accomplish?
Screenshots
What approach did you choose and why?
Merge checklist