Form editor: real-time input mapping panel with palette highlighting#357
Form editor: real-time input mapping panel with palette highlighting#357morissette wants to merge 9 commits intoCodeForPhilly:mainfrom
Conversation
When building a form, users had no visibility into what inputs needed
to be mapped or what form component types to use for each input.
- formSchemaUtils: add extractInputPaths() to flatten a JSONSchema7
inputDefinition into { path, type } pairs
- pathOptionsService: export TYPE_COMPATIBILITY, COMPONENT_TYPE_LABELS,
and compatibleComponentLabels() for use outside Form-JS modules
- SelectedEligibilityCheck: render Required Form Inputs section showing
each input path, its type, and compatible component types (filling in
the existing placeholder comment)
- FormValidationDrawer: show compatible component type hints on each
missing input row so users know what field type to add
- FormValidationDrawer: rename trigger to 'Required Inputs' and add a
red badge showing the missing input count at a glance
- Replace stacked card layout with horizontal pill strip in form editor toolbar - Hover/click a pill to dim incompatible palette fields and ring compatible ones via injected <style> tag targeting .fjs-palette-field[data-field-type] - Click to pin the highlight; click again to unpin - Add ⓘ indicator and tooltip showing compatible component types - Add full ARIA support: role=button, tabIndex, aria-pressed, aria-label, onKeyDown (Enter/Space), onFocus/onBlur for keyboard palette highlight, role=tooltip, group-focus-within tooltip visibility, focus-visible ring - Remove stray console.log statements from customKeyDropdownProvider and pathOptionsService - Fix extractInputPaths to skip "null" in JSON Schema union types before resolving the leaf type (e.g. ["null","string"] now correctly resolves to "string") Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Justin-MacIntosh
left a comment
There was a problem hiding this comment.
I really like the UX ideas here!! I personally was struggling to find a good place to put the form paths (and the save button), and the idea to highlight the relevant Form Components is really good. This needs a pass in terms of the functionality though, there are a few missing pieces before this is ready to go in.
| { | ||
| // Place to display information about expected inputs for check | ||
| } | ||
| <Show when={extractInputPaths(checkConfig().inputDefinition).length > 0}> |
There was a problem hiding this comment.
issue: Unfortunately, this new function doesn't help when determining which FormPaths need to be filled out for a Selected EligibilityCheck. checkConfig().inputDefinition is the inputs "pre-transformation".
Right now the transformation logic for transforming the InputSchema of a Check to FormPath objects exists in the backend under FormDataTransformer and InputSchemaService. It's only accessible in the Frontend via the endpoint /screener/{screenerId}/form-paths, which returns FormPaths for a whole Screener, not individual checks. To do what you're doing here (which is a good idea for the UX!) we'll likely need another endpoint for returning the FormPaths of a CheckConfig object.
Critical fixes:
- setFormSchema({ ...e.schema }) to force new reference and fix
InputsPanel not updating when dropdown selections change
- extractInputPaths in SelectedEligibilityCheck now calls
transformInputDefinitionSchema first so people/enrollments paths
reflect parameter-keyed shape, matching what the backend returns
- Tooltips now render via Portal to escape overflow-x:auto clipping;
position is computed from getBoundingClientRect on hover/focus
Nits:
- Extract updatePaletteHighlight as a module-level function for readability
- Extract shared InputPill component; red/green pills no longer duplicate
markup
- Add py-1.5 px-0.5 to pill scroll container so pinned rings aren't
clipped at edges
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The previous createEffect on (hoveredPath ?? pinnedPath) was indirect:
clicking a pill set pinnedPath, which triggered the effect, which called
setHighlightedTypes with TYPE_COMPATIBILITY[type] — often the same array
reference as the current value, so Solid.js { equals: === } would skip
the downstream FormEditorView effect and updatePaletteHighlight would
not re-run.
Fix:
- Remove the derived createEffect entirely
- Add a `highlight(path | null)` helper called directly from each event
handler — hover, hover-end, and click each explicitly drive the palette
- Set equals: false on highlightedTypes so every setHighlightedTypes
call always propagates to updatePaletteHighlight regardless of
array reference equality
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Pinned pills now show a colored box-shadow glow (red for missing, green for mapped) alongside the existing ring, making it visually clear which pill is active and that clicking again will deactivate it. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Pinned pills now use border-red-500/border-green-500 instead of the default -300 shade, giving a stronger visual distinction alongside the existing ring and glow. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
form-js loads Bootstrap CSS which has `.border { border: 1px solid #dee2e6 !important }`
overriding Tailwind's border-color utilities. Switch to darkening the
background (bg-red-100 / bg-green-100 vs bg-*-50) to distinguish the
pinned state, which is not affected by that rule.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Destructuring props in SolidJS evaluates each getter once at creation time, producing a static snapshot. isPinned was destructured, so it never updated after the initial render — clicking a pill set pinnedPath in the parent but InputPill kept aria-pressed="false" and never applied the ring/glow/bg classes. Switch InputPill to the props-object pattern so every access of props.isPinned reads the live reactive getter. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Remove ring and glow; pinned pills are now distinguished solely by background darkening (bg-*-100 vs bg-*-50). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
Thanks for the thorough review! Here's what was addressed: Issues
Nits
Additional fixes found during review
|
|
@morissette Interesting ideas! I'm the representative Benefits Analyst who configures screeners as a non-coder/SE. For me, I prefer the hidden validation panel. As we add more checks and the ability to configure longer, more complicated screeners, my concern is that it will look cluttered at the top of the page with a long list of checks. I'm adding questions first, then using the key drop down to connect the check. It's a bit of a chicken-and-egg issue. I like highlighting the possible inputs you can add based on the check, but configuring the screener is about adding questions first then connecting the checks. It's tough for me to imagine it would be intuitive for a non-software engineer Benefits Analyst to do that in reverse. BTW we also have a Slack UX channel to discuss UX ideas. |
|
The biggest issue for me was when I added the inputs I didn't remember all the inputs and the less prominent validate button in the bottom right wasn't intuitively known to check. In addition I had to add like every input type to find the matching key to input type. Thanks for the slack mention; joined! |

What this does
While building a form for a screener, I ran into a frustrating UX gap: there was no way to see what data fields the benefits required, or which form component types were compatible, without leaving the Form Editor tab. This PR adds real-time visibility directly in the form editor toolbar.
Changes
Compact input status strip (top of Form Editor)
A horizontal pill bar replaces the previous hidden/drawer validation approach. It shows every required input path at a glance — no button click needed.
Palette highlighting on hover/click
Hovering a pill dims incompatible component types in the left-hand palette and outlines compatible ones with a sky-blue ring — so you know exactly what to drag onto the canvas.
Clicking a pill pins the highlight so it stays visible while you work. Click again to unpin.
Tooltip
A tooltip on each pill lists the compatible component types in human-readable form ("Text field", "Dropdown", etc.).
ARIA / keyboard support
role="button",tabIndex,aria-pressed(pin state),aria-label(path + status + compatible types)role="tooltip"on tooltip divs; tooltip visible ongroup-focus-withinas well as hover✗✓ⓘ) markedaria-hiddenBug fix
extractInputPathsnow correctly handles nullable JSON Schema union types (["null", "string"]) by skipping"null"before resolving the leaf type — previously it would take index 0, silently produce no compatible components for optional fields, and break the key dropdown filter.Cleanup
console.logstatements incustomKeyDropdownProviderandpathOptionsServiceScreenshot
Form editor showing the input strip with one missing input (red), two mapped inputs (green), and the boolean-compatible palette components highlighted after hovering
simpleChecks.tenYearTaxAbatement:Testing