replace pipe-value sniffing with explicit list field options - #1660
replace pipe-value sniffing with explicit list field options#1660MattBudz wants to merge 12 commits into
Conversation
The Fields editor used to render a <select> only when a field's raw value happened to look like a pipe-separated string, and only for brand-new records. RTPs no longer generate that shape (List fields are now a proper type with explicit values), so the heuristic was dead weight and permanently lost the dropdown after a record's first save. FieldsController/FieldsHelper now accept an explicit field_options map and render a select whenever a field's name has options and its current value is blank or already one of them, independent of whether the record is new.
Note Templates (config/templates/notes/*.txt) hand-author pipe separated values (e.g. "Linux | OSX | Windows") to get a dropdown when creating a new record from them, independently of RTPs. The List-field lookup added in the previous commit is now tried first; when a field has no explicit list options, this restores the original new-record-only, pipe-shape based check as a fallback.
etdsoft
left a comment
There was a problem hiding this comment.
Automated peer review -- PR #1660
Reviewers: Claude (sub-agent 1), Codex (sub-agent 2)
Verdict: request-changes
Findings: 1 High, 0 Medium, 2 Low
What I checked
This is an automated peer code review by two independent LLM sub-agents reading the same rubric. Findings flagged by both agents are higher confidence; findings flagged by only one are still worth your attention. The verdict is computed from severity counts -- there is no editorial judgment in the merge step.
Both reviewers agreed
(no shared findings)
Claude only
F1 [Low|consistency] app/helpers/fields_helper.rb:12 -- helper methods not alphabetized within sections
What: In the public section, render_dropdown? (line 2) is followed by dropdown_options (line 12). In the private section, list_field_options (line 18) is followed by has_liquid_filters? (line 22). Both sections are reverse-alphabetical.
Why: .claude/CLAUDE.md → "Code style" → "Alphabetical order within sections" says method definitions must be sorted alphabetically within their logical section. The pre-existing has_liquid_filters? was previously the only private method, so ordering wasn't visible; this PR introduces dropdown_options and list_field_options and both land out of order.
How to fix: Swap the two public methods so dropdown_options is defined before render_dropdown?, and swap the two private methods so has_liquid_filters? is defined before list_field_options.
F2 [Low|tests] spec/helpers/fields_helper_spec.rb:21 -- spec passes for the wrong reason
What: The example "returns false for a field with no matching options" sets @field_options = { 'Risk' => ... } and asserts render_dropdown?('Status', 'Open') is falsey. Since @field_options['Status'] is nil, execution falls to the else-branch, which returns false only because @allow_dropdown was never assigned (nil). The assertion is not testing the "field_options entry missing" branch independently.
Why: If a future change defaulted @allow_dropdown to truthy, this test would silently start passing for a different reason (or failing without clearly indicating which branch broke).
How to fix: Either (a) add assign(:allow_dropdown, true) so the assertion is clearly depending on the field_options lookup (not the unset @allow_dropdown), or (b) rename the example to "falls back to the pipe-value check when the field has no explicit options" and add assign(:allow_dropdown, true) + a pipe-separated value to make the fallback path explicitly exercised.
Codex only
F3 [High|correctness] app/views/fields/_field.html.erb:24 -- blank list values silently submit as the first option
What: When render_dropdown? returns true for a blank explicit list-field value (CE's new path: value.blank? → true), the select renders dropdown_options(field_name, value) with { selected: value } but no blank option. A browser submits the first <option> when no option matches the selected value.
Why: For a new record with an explicit list field (e.g. Risk = High/Medium/Low), opening the Fields tab and saving without touching the Risk field submits "High" -- silently writing a non-empty value into a field the user never touched. This is data-loss behavior: user intent (leave blank) is overwritten by browser default.
How to fix: Add include_blank: value.blank? to the options hash so a blank option is rendered (and pre-selected) when the current value is empty:
<%= select :item_form, "field_value_#{index}",
dropdown_options(field_name, value),
{ selected: value, include_blank: value.blank? },
{ class: 'form-select', data: { behavior: 'preview-enabled' }, tabindex: index + 1 } %>Add a helper/view spec that renders a blank explicit list field and asserts the submitted value is blank (not the first option) when the user does not interact.
Notes
- CE never populates
data-field-optionson any textarea today (grep confirms), so_fieldOptionsisundefinedclient-side and omitted from the POST -- the new High finding only manifests when Pro wires real RTP List field options. Still worth fixing here before the Pro follow-up ships. select ... { selected: value }for the legacy pipe path passes the full pipe string as the selected value, which matches none of the split options; the browser falls back to the first<option>, matching previous behavior. Not a new regression.
Reviewed automatically. Raw outputs in the shared product reviews directory.
Rendering a select with a blank value and no include_blank option left the browser defaulting to the first option, silently writing a value the user never chose.
The example asserted false for a field missing from field_options, but it passed because @allow_dropdown was never assigned, not because of the fallback logic being tested.
Caitlin: the pipe-joined 'Default' template value never matched options.include?(value), and keeping two near-duplicate methods in sync was the actual source of confusion, not just the naming.
…radis/dradis-ce into fields/render-select-from-list-options
Switching from Source back to Fields, or restoring an unsaved draft via auto-save, re-rendered fields with allow-dropdown hardcoded to false/undefined instead of the value set on init. This silently downgraded pipe-separated list fields to plain textareas.
Summary
replace pipe-value sniffing with explicit list field options, keeping the pipe-value check as a fallback
Testing steps
#[Name]#followed by the value you typed#[OS]#\nLinux | OSX | Windows?template=<name>)<select>with Linux/OSX/Windows as optionsOther Information
This adds a generic, explicit
field_optionslookup (unused by CE itself, since CE has no List-field concept) as the primary mechanism, and keeps the original pipe-separated-value heuristic as a fallback when no explicit options are set for a field. The fallback still only applies to new records, exactly as before.The fallback is required because Note Templates (
config/templates/notes/*.txt, managed under Admin > Templates > Notes) are a separate, RTP-independent CE/Pro feature that relies on hand-authored pipe-separated values to get a dropdown when creating a new record from a template. An earlier version of this PR removed the pipe-value check entirely, which broke this (caught by CI:spec/features/issues_spec.rb"preloads the editor with the template").Confirmed via automated specs and manual browser testing that both paths (Note Templates and the new explicit lookup) work as expected.
Check List
Added a CHANGELOG entry(no user-visible CE change)