Replacement field components¶
This page covers the field components re-exported from
replacement_components/field_components/index.js — local copies and forks of
field components that exist in invenio_rdm_records (and a few in
react-invenio-forms). They ship in this package so the modular deposit form
can use shared widgets and consistent UX without patching
node_modules.
For how these plug into layout and the components registry, see Built-in field widget components.
Why these replacements exist¶
A small number of cross-cutting issues account for almost every replacement on this page. Knowing them up front makes the catalog below much easier to read:
Touched-aware error visibility. Upstream field components don’t all gate visible errors on Formik’s
touchedflag the same wayTextFielddoes. The replacements use a sharedgetFieldErrorsForDisplayhelper and explicitly marktouchedon controls that aren’t plain Formik<Field>s (PID radios and the unmanaged identifier input, search dropdowns, the creators/contributors modal). The result: validation errors only appear after a user has interacted with the field, consistently across the form.Help text above and below the widget.
react-invenio-formstypically collapseshelpText ?? descriptioninto a single string rendered below the field. The replacements treatdescription(above the control) andhelpText(below) as separate slots, matching the convention used by the rest of this package’s section components.Keyboard / focus a11y. Search dropdowns can drop focus into a hard-to- recover state after selection; modal lifecycle and ID/name plumbing on non-
<Field>controls also affect screen-reader and keyboard behavior. The replacements add small, opt-in fixes (e.g.focusFieldPathAfterSelectonRemoteSelectField) and keep blur/touched wiring sensible. Inline reorder buttons for the flat creator/contributor list live with that component — see Alternate components.Layout and registry hooks. Layout config (
label_modifications,icon_modifications, etc.) needs a consistent path fromFieldComponentWrapperinto the inner widget. The replacements acceptlabelIcon(aligned withinvenio_rdm_records) and forward props predictably.
Most of the “stock copy” entries in The replacement field components
below exist only so they can import this package’s TextField /
SelectField / RemoteSelectField instead of the upstream defaults. They
aren’t doing anything novel themselves — they’re conduits for the changes
above.
Warning
Draft sections may evolve as upstream InvenioRDM changes. When in doubt, read the file header in each source module.
Top-level replacement widgets¶
replacement_components/index.js re-exports the core widgets and a small set
of stock-name adapters:
Core widgets:
TextField,TextArea,SelectField,RemoteSelectField,MultiInput.Stock-name adapters:
Input,Dropdown,AutocompleteDropdown. These are thin wrappers that delegate toTextField,SelectField, andRemoteSelectField, respectively. They exist so backend custom-fieldui_widgetnames (Input,Dropdown,AutocompleteDropdown) resolve to the touched-aware local widgets without you having to change those names in YAML or inRDM_CUSTOM_FIELDS_UI.
All of these expose description (rendered above the control) and
helpText (rendered below) as separate props. Two known exceptions keep
their own helptext behavior: PIDField and ResourceTypeSelectorField.
SelectField and RemoteSelectField — notes worth knowing¶
A few behaviors are worth knowing if you wrap or extend these widgets:
SelectFieldmarks touched on blur. Formik’shandleBlurdecides what to mark fromevent.target.name/id, and a search dropdown’s blur event often doesn’t carry the Formik path. The localSelectFieldcallsform.setFieldTouched(fieldPath, true, false)on blur in addition tohandleBlur, so the touched-aware error gating works.SelectFieldchains a caller-providedonBlur. If you pass anonBlurprop (e.g. fromRemoteSelectField), it runs afterhandleBlurandsetFieldTouched. You can extend blur behavior without accidentally dropping touched-marking. The same chaining applies toonFocus(onFocus(e, { formikProps })).RemoteSelectFieldopt-in props. All default tofalse/ unset:commitSearchOnBlur— for single-value fields, blur commits trimmed search text as a free-text choice. Doesn’t require Semantic UI’sallowAdditions. Also reconstructs mid-typeahead state on focus: seeds the search input from the Formik field value (text selected on focus, like a normal text input); display text updates on each keystroke while remote fetches stay debounced. Focus alone does not fetch.hideAdditionMenuItem— setsallowAdditions={false}on theForm.Dropdown, hiding Semantic UI’s synthetic “Add …” row. Pair withcommitSearchOnBlur(or list-only values) when free text must still apply.focusFieldPathAfterSelect— DOMid(or name path) to focus after a selection. Used by the flat creators UI to jump from family-name to given-name after picking a person.
Label survives remount.
RemoteSelectFieldwritesui.<fieldPath> = { id, title_l10n }for the selected value(s) so the visible label can be recovered frominitialSuggestionsafter remount, without changing the canonical value shape.
FieldComponentWrapper and labelIcon¶
Built-in section components wrap their inner widget in
FieldComponentWrapper, which merges layout config (label_modifications,
icon_modifications, etc.) and forwards props to the inner widget via
React.cloneElement.
The wrapper passes the field’s label icon as labelIcon (matching
invenio_rdm_records field components such as AccessRightField). Legacy
icon props on the wrapper or merged into custom-field props are still folded
into the computed labelIcon, so existing custom_fields.ui / YAML using
icon continues to work.
Replacement TextField, TextArea, and MultiInput accept
labelIcon only for that label icon. The stock-name adapters Input
and Dropdown may still accept stock icon and map it to
FieldLabel.
The replacement field components¶
The barrel is at:
invenio_modular_deposit_form/assets/semantic-ui/js/invenio_modular_deposit_form/replacement_components/field_components/index.js
Stock copies that only swap in the local input widgets¶
The following are mostly thin copies of the upstream field whose only
meaningful change is to import this package’s TextField / SelectField /
RemoteSelectField (and a few helpers like emptyDate / emptyIdentifier)
so the touched-aware widgets and dual help text apply throughout:
AdditionalDescriptionsFieldAdditionalTitlesFieldCopyrightsField(passes throughlabelIconfrom layout; upstream hardcodes the icon onFieldLabel)DatesFieldDescriptionsFieldIdentifiersFieldLanguagesField(uses localRemoteSelectFieldinstead of the stock select)PublisherFieldRelatedWorksField(rowResourceTypeFieldfrom this folder, replacementSelectField)ResourceTypeFieldTitlesFieldVersionField
If you customize layouts, you’d typically reach for these via the components registry — see Built-in field widget components.
Forks with substantive behavior changes¶
CreatibutorsField— uses a localCreatibutorsModalso anonModalClosecallback runs whenever the modal closes (cancel, dismiss, save). That’s how the parent gets a chance tosetFieldTouched, so any existing errors become visible after editing. Item, type, and util components are still imported from@js/invenio_rdm_records.For an inline (non-modal) alternative with extra a11y affordances (Up/Down reorder buttons, “Add myself”, focus management), see the flat creator UI in Alternate components.
PIDField— the largest fork; see below.
PIDField (touched and DOI selection)¶
The replacement PIDField exists for two reasons.
Touched-aware errors. Stock PIDField doesn’t wrap the identifier input
or the managed/unmanaged radios in plain Formik <Field> controls, so nothing
was ever marking the PID path “touched”. Without that, pids.<scheme> errors
could appear before the user interacted with the field — out of step with
every other field in the form. The replacement marks touched explicitly at
three interaction points:
Blur of the unmanaged identifier input —
setFieldTouched(fieldPath, true, true)(touch + run validation).Toggle of the managed/unmanaged radios —
setFieldTouched(fieldPath, false, false)(untouched, no validation on that call) so switching branches doesn’t immediately validate or show errors for an empty PID. Validation re-engages when the user actually edits the new branch.Toggle of the optional-DOI radios (
managed/unmanaged/not_needed) — same pattern:setFieldTouched(fieldPath, false, false), plus a write tovalues.ui.pids.doi.managed_selectionso the choice survives a remount or a clearedpidsobject.
Branch state survives remount. Stock RequiredPIDField keeps “is the user
on the managed or unmanaged branch?” in component state (isManagedSelected),
which is lost on remount and can disagree with the actual Formik values. The
replacement persists the choice in values.ui.<fieldPath>.managed_selection
(values: "managed" / "unmanaged") so the right radio stays selected after
remount. OptionalPIDField does the same with
"managed" / "unmanaged" / "not_needed" so optional DOI doesn’t snap back
to “managed” after a remount when the user had picked something else.
Note
Stock PIDField has no componentDidMount seeding from default_selected.
The replacement adds limited mount-time seeding for RequiredPIDField
only — it sets { provider: "external", identifier: "" } for "yes", or
clears the value for "no" (only when the existing value isn’t already an
explicit unmanaged shape). OptionalPIDField deliberately does not seed on
mount, so an empty optional DOI never validates as an external identifier.
Where the touched wiring lives¶
If you’re customizing PID behavior or debugging visible errors, the relevant
files under
replacement_components/field_components/PIDField/ are:
pid_components/fieldErrorsForDisplay.js— the touched-aware error helper used on the label row and identifier components.pickDisplayableErrormerges nested Yup messages (e.g.errors.pids.doi.identifier) so the FastField bound topids.doistill receives a single string for SUI’serror=prop.RequiredPIDField.js— managed/unmanaged radio handler; mount-time seeding (described above);componentDidUpdatekeepsdraft_managed_pid_backupin sync while the managed branch is selected.OptionalPIDField.js— optional-DOI radio handler; no mount-time seeding; the unmanaged radio avoidsprovider: "external"until the user actually types in the unmanaged identifier.pid_components/UnmanagedIdentifierCmp.js—onBlurcallssetFieldTouched(fieldPath, true, true). The Semantic UIForm.Inputreceivesname={field.name || fieldPath}so Formik’s blur handler can identify the path correctly even when the inner element doesn’t carry it.
The deposit form imports PIDField from this tree (e.g. DoiComponent in
field_components.jsx), so DOI/PID errors don’t appear before touch in a way
that disagrees with the rest of the form.
Form feedback components (cross-reference)¶
The form-feedback UI used in this package’s layouts (FormFeedback,
FormFeedbackSummary) lives under replacement_components/alternate_components/
rather than replacement_components/field_components/. See
Form feedback (errors and action state)
for behavior and props.
Internal notes¶
Design notes for what would need to change upstream to retire each replacement
live in docs/internal/upstream-replacement-removal-checklist.md (not
published as part of the manual). Notes on the flat creatibutor name UX live
in docs/internal/creatibutors-field-flat-person-names.md.