diff --git a/src/lib/forms/RichInputField.js b/src/lib/forms/RichInputField.js
index 456b8923..40271f28 100644
--- a/src/lib/forms/RichInputField.js
+++ b/src/lib/forms/RichInputField.js
@@ -15,7 +15,8 @@ import { Form } from "semantic-ui-react";
export class RichInputField extends Component {
renderFormField = (formikBag) => {
- const { fieldPath, label, required, className, editor, editorConfig } = this.props;
+ const { fieldPath, label, required, className, editor, editorConfig, disabled } =
+ this.props;
const value = getIn(formikBag.form.values, fieldPath, "");
const initialValue = getIn(formikBag.form.initialValues, fieldPath, "");
const error =
@@ -28,6 +29,7 @@ export class RichInputField extends Component {
@@ -74,6 +76,7 @@ RichInputField.propTypes = {
label: PropTypes.oneOfType([PropTypes.string, PropTypes.object]),
required: PropTypes.bool,
editorConfig: PropTypes.object,
+ disabled: PropTypes.bool,
};
RichInputField.defaultProps = {
@@ -83,4 +86,5 @@ RichInputField.defaultProps = {
label: "",
editor: undefined,
editorConfig: undefined,
+ disabled: false,
};
diff --git a/src/lib/forms/SelectField.js b/src/lib/forms/SelectField.js
index 12575c5f..44d811ac 100644
--- a/src/lib/forms/SelectField.js
+++ b/src/lib/forms/SelectField.js
@@ -90,15 +90,18 @@ export class SelectField extends Component {
};
render() {
- const { optimized, fieldPath, ...uiProps } = this.props;
+ const { optimized, fieldPath, helpText, ...uiProps } = this.props;
const FormikField = optimized ? FastField : Field;
return (
-
+ <>
+
+ {helpText && }
+ >
);
}
}
@@ -113,6 +116,7 @@ SelectField.propTypes = {
onChange: PropTypes.func,
onAddItem: PropTypes.func,
multiple: PropTypes.bool,
+ helpText: PropTypes.string,
};
SelectField.defaultProps = {
@@ -123,4 +127,5 @@ SelectField.defaultProps = {
onChange: undefined,
onAddItem: undefined,
multiple: false,
+ helpText: undefined,
};
diff --git a/src/lib/forms/widgets/array/Array.js b/src/lib/forms/widgets/array/Array.js
index f144e04a..31fe4d9a 100644
--- a/src/lib/forms/widgets/array/Array.js
+++ b/src/lib/forms/widgets/array/Array.js
@@ -1,10 +1,14 @@
import React, { Component } from "react";
import PropTypes from "prop-types";
-
import { FieldLabel } from "../../FieldLabel";
import { ArrayField } from "../../ArrayField";
+import {
+ fieldCommonProps,
+ createShowHideComponent,
+ createDynamicOverridableComponent,
+} from "../../../utils";
-export default class Array extends Component {
+class _Array extends Component {
render() {
const {
fieldPath,
@@ -17,16 +21,21 @@ export default class Array extends Component {
addButtonLabel,
defaultNewValue,
className,
+ helpText: helpTextProp,
+ labelIcon: labelIconProp,
} = this.props;
+ const helpText = helpTextProp ?? description;
+ const labelIcon = labelIconProp ?? icon;
+
return (
}
+ label={}
addButtonLabel={addButtonLabel}
defaultNewValue={defaultNewValue}
className={className}
@@ -37,22 +46,26 @@ export default class Array extends Component {
}
}
-Array.propTypes = {
- fieldPath: PropTypes.string.isRequired,
- label: PropTypes.string.isRequired,
- description: PropTypes.string.isRequired,
- icon: PropTypes.string,
- required: PropTypes.bool,
- disabled: PropTypes.bool,
+_Array.propTypes = {
children: PropTypes.func.isRequired,
addButtonLabel: PropTypes.string.isRequired,
defaultNewValue: PropTypes.oneOfType([PropTypes.string, PropTypes.object]).isRequired,
className: PropTypes.string,
+ /**
+ * @deprecated Use `labelIcon` instead
+ */
+ icon: PropTypes.string,
+ /**
+ * @deprecated Use `helpText` instead
+ */
+ description: PropTypes.string.isRequired,
+ ...fieldCommonProps,
};
-Array.defaultProps = {
- icon: undefined,
- required: false,
- disabled: false,
+_Array.defaultProps = {
className: "",
+ icon: undefined,
};
+
+export const ArrayComponent = createShowHideComponent(_Array);
+export const Array = createDynamicOverridableComponent(ArrayComponent);
diff --git a/src/lib/forms/widgets/array/index.js b/src/lib/forms/widgets/array/index.js
index b1fd8856..d47e11a1 100644
--- a/src/lib/forms/widgets/array/index.js
+++ b/src/lib/forms/widgets/array/index.js
@@ -1 +1 @@
-export { default as Array } from "./Array";
+export { Array, ArrayComponent } from "./Array";
diff --git a/src/lib/forms/widgets/select/AutocompleteDropdown.js b/src/lib/forms/widgets/select/AutocompleteDropdown.js
index 336957f5..ec3ba0c8 100644
--- a/src/lib/forms/widgets/select/AutocompleteDropdown.js
+++ b/src/lib/forms/widgets/select/AutocompleteDropdown.js
@@ -6,8 +6,13 @@ import _isArray from "lodash/isArray";
import { Field } from "formik";
import { FieldLabel } from "../../FieldLabel";
import { RemoteSelectField } from "../../RemoteSelectField";
+import {
+ createDynamicOverridableComponent,
+ createShowHideComponent,
+ fieldCommonProps,
+} from "../../../utils";
-export default class AutocompleteDropdown extends Component {
+class _AutocompleteDropdownComponent extends Component {
render() {
const {
description,
@@ -20,10 +25,17 @@ export default class AutocompleteDropdown extends Component {
multiple,
autocompleteFrom,
autocompleteFromAcceptHeader,
+ helpText: helpTextProp,
+ labelIcon: labelIconProp,
+ disabled,
} = this.props;
+
+ const helpText = helpTextProp ?? description;
+ const labelIcon = labelIconProp ?? icon;
+
return (
<>
-
+
{({ form: { values } }) => {
return (
@@ -38,6 +50,8 @@ export default class AutocompleteDropdown extends Component {
suggestionAPIHeaders={{
Accept: autocompleteFromAcceptHeader,
}}
+ disabled={disabled}
+ helpText={helpText}
serializeSuggestions={(suggestions) => {
return _isArray(suggestions)
? suggestions.map((item) => ({
@@ -58,29 +72,39 @@ export default class AutocompleteDropdown extends Component {
);
}}
- {description && }
>
);
}
}
-AutocompleteDropdown.propTypes = {
- fieldPath: PropTypes.string.isRequired,
- label: PropTypes.string.isRequired,
+_AutocompleteDropdownComponent.propTypes = {
placeholder: PropTypes.string.isRequired,
- description: PropTypes.string.isRequired,
autocompleteFrom: PropTypes.string.isRequired,
autocompleteFromAcceptHeader: PropTypes.string,
- icon: PropTypes.string,
clearable: PropTypes.bool,
multiple: PropTypes.bool,
- required: PropTypes.bool,
+ /**
+ * @deprecated Use `helpText` instead
+ */
+ description: PropTypes.string,
+ /**
+ * @deprecated Use `labelIcon` instead
+ */
+ icon: PropTypes.string,
+ ...fieldCommonProps,
};
-AutocompleteDropdown.defaultProps = {
- icon: undefined,
+_AutocompleteDropdownComponent.defaultProps = {
autocompleteFromAcceptHeader: "application/vnd.inveniordm.v1+json",
clearable: false,
multiple: false,
- required: false,
+ icon: undefined,
+ description: undefined,
};
+
+export const AutocompleteDropdownComponent = createShowHideComponent(
+ _AutocompleteDropdownComponent
+);
+export const AutocompleteDropdown = createDynamicOverridableComponent(
+ AutocompleteDropdownComponent
+);
diff --git a/src/lib/forms/widgets/select/Dropdown.js b/src/lib/forms/widgets/select/Dropdown.js
index 4ce9dc56..119df175 100644
--- a/src/lib/forms/widgets/select/Dropdown.js
+++ b/src/lib/forms/widgets/select/Dropdown.js
@@ -1,10 +1,14 @@
import React, { Component } from "react";
import PropTypes from "prop-types";
-
import { FieldLabel } from "../../FieldLabel";
import { SelectField } from "../../SelectField";
+import {
+ createDynamicOverridableComponent,
+ createShowHideComponent,
+ fieldCommonProps,
+} from "../../../utils";
-export default class Dropdown extends Component {
+class _DropdownComponent extends Component {
serializeOptions = (options) =>
options?.map((option) => ({
text: option.title_l10n,
@@ -15,62 +19,74 @@ export default class Dropdown extends Component {
render() {
const {
description,
+ helpText: helpTextProp,
placeholder,
fieldPath,
label,
icon,
+ labelIcon: labelIconProp,
options,
search,
multiple,
clearable,
required,
+ disabled,
} = this.props;
+
+ const helpText = helpTextProp ?? description;
+ const labelIcon = labelIconProp ?? icon;
+
return (
- <>
- }
- options={this.serializeOptions(options)}
- search={search}
- aria-label={label}
- multiple={multiple}
- placeholder={{
- role: "option",
- content: placeholder,
- }}
- clearable={clearable}
- required={required}
- optimized
- defaultValue={multiple ? [] : ""}
- />
- {description && }
- >
+ }
+ options={this.serializeOptions(options)}
+ search={search}
+ aria-label={label}
+ multiple={multiple}
+ disabled={disabled}
+ placeholder={{
+ role: "option",
+ content: placeholder,
+ }}
+ clearable={clearable}
+ required={required}
+ optimized
+ defaultValue={multiple ? [] : ""}
+ helpText={helpText}
+ />
);
}
}
-Dropdown.propTypes = {
- fieldPath: PropTypes.string.isRequired,
- label: PropTypes.string.isRequired,
- placeholder: PropTypes.string.isRequired,
- description: PropTypes.string.isRequired,
+_DropdownComponent.propTypes = {
options: PropTypes.arrayOf(
PropTypes.shape({
id: PropTypes.string.isRequired,
title_l10n: PropTypes.string.isRequired,
})
).isRequired,
- icon: PropTypes.string,
search: PropTypes.bool,
multiple: PropTypes.bool,
clearable: PropTypes.bool,
- required: PropTypes.bool,
+ /**
+ * @deprecated Use `helpText` instead
+ */
+ description: PropTypes.string,
+ /**
+ * @deprecated Use `labelIcon` instead
+ */
+ icon: PropTypes.string,
+ ...fieldCommonProps,
};
-Dropdown.defaultProps = {
+_DropdownComponent.defaultProps = {
icon: undefined,
search: false,
multiple: false,
clearable: true,
- required: false,
+ description: undefined,
};
+
+export const DropdownComponent = createShowHideComponent(_DropdownComponent);
+export const Dropdown = createDynamicOverridableComponent(DropdownComponent);
diff --git a/src/lib/forms/widgets/select/index.js b/src/lib/forms/widgets/select/index.js
index 6081f4b9..ee9c34db 100644
--- a/src/lib/forms/widgets/select/index.js
+++ b/src/lib/forms/widgets/select/index.js
@@ -1,2 +1,5 @@
-export { default as AutocompleteDropdown } from "./AutocompleteDropdown";
-export { default as Dropdown } from "./Dropdown";
+export {
+ AutocompleteDropdown,
+ AutocompleteDropdownComponent,
+} from "./AutocompleteDropdown";
+export { Dropdown, DropdownComponent } from "./Dropdown";
diff --git a/src/lib/forms/widgets/text/BooleanCheckbox.js b/src/lib/forms/widgets/text/BooleanCheckbox.js
index 533f828d..468754a9 100644
--- a/src/lib/forms/widgets/text/BooleanCheckbox.js
+++ b/src/lib/forms/widgets/text/BooleanCheckbox.js
@@ -12,8 +12,13 @@ import { FieldLabel } from "../../FieldLabel";
import { RadioField } from "../../RadioField";
import { useField } from "formik";
+import {
+ createDynamicOverridableComponent,
+ createShowHideComponent,
+ fieldCommonProps,
+} from "../../../utils";
-export default function BooleanCheckbox({
+function _BooleanCheckboxComponent({
description,
icon,
falseLabel,
@@ -21,14 +26,19 @@ export default function BooleanCheckbox({
label,
trueLabel,
required,
+ helpText: helpTextProp,
+ labelIcon: labelIconProp,
}) {
+ const helpText = helpTextProp ?? description;
+ const labelIcon = labelIconProp ?? icon;
+
// eslint-disable-next-line no-unused-vars
const [_, meta] = useField(fieldPath);
return (
<>
-
+
)}
- {description && }
+ {helpText && }
>
);
}
-BooleanCheckbox.propTypes = {
- fieldPath: PropTypes.string.isRequired,
- label: PropTypes.string.isRequired,
+_BooleanCheckboxComponent.propTypes = {
trueLabel: PropTypes.string.isRequired,
falseLabel: PropTypes.string.isRequired,
+ /**
+ * @deprecated Use `helpText` instead
+ */
description: PropTypes.string.isRequired,
+ /**
+ * @deprecated Use `labelIcon` instead
+ */
icon: PropTypes.string,
- required: PropTypes.bool,
+ ...fieldCommonProps,
};
-BooleanCheckbox.defaultProps = {
+_BooleanCheckboxComponent.defaultProps = {
icon: undefined,
required: false,
};
+
+export const BooleanCheckboxComponent = createShowHideComponent(
+ _BooleanCheckboxComponent
+);
+export const BooleanCheckbox = createDynamicOverridableComponent(
+ BooleanCheckboxComponent
+);
diff --git a/src/lib/forms/widgets/text/Input.js b/src/lib/forms/widgets/text/Input.js
index 420af472..0ae90521 100644
--- a/src/lib/forms/widgets/text/Input.js
+++ b/src/lib/forms/widgets/text/Input.js
@@ -1,10 +1,14 @@
import React, { Component } from "react";
import PropTypes from "prop-types";
-
import { FieldLabel } from "../../FieldLabel";
import { TextField } from "../../TextField";
+import {
+ createShowHideComponent,
+ createDynamicOverridableComponent,
+ fieldCommonProps,
+} from "../../../utils/";
-export default class Input extends Component {
+class _InputComponent extends Component {
render() {
const {
fieldPath,
@@ -15,16 +19,21 @@ export default class Input extends Component {
description,
disabled,
type,
+ helpText: helpTextProp,
+ labelIcon: labelIconProp,
} = this.props;
+ const helpText = helpTextProp ?? description;
+ const labelIcon = labelIconProp ?? icon;
+
return (
}
+ label={}
placeholder={placeholder}
type={type}
/>
@@ -32,20 +41,23 @@ export default class Input extends Component {
}
}
-Input.propTypes = {
- fieldPath: PropTypes.string.isRequired,
- label: PropTypes.string.isRequired,
- placeholder: PropTypes.string.isRequired,
+_InputComponent.propTypes = {
+ /**
+ * @deprecated Use `helpText` instead
+ */
description: PropTypes.string.isRequired,
+ /**
+ * @deprecated Use `labelIcon` instead
+ */
icon: PropTypes.string,
- required: PropTypes.bool,
- disabled: PropTypes.bool,
type: PropTypes.string,
+ ...fieldCommonProps,
};
-Input.defaultProps = {
+_InputComponent.defaultProps = {
icon: undefined,
- required: false,
- disabled: false,
type: "input",
};
+
+export const InputComponent = createShowHideComponent(_InputComponent);
+export const Input = createDynamicOverridableComponent(InputComponent);
diff --git a/src/lib/forms/widgets/text/MultiInput.js b/src/lib/forms/widgets/text/MultiInput.js
index 4a71a082..d83cbd3d 100644
--- a/src/lib/forms/widgets/text/MultiInput.js
+++ b/src/lib/forms/widgets/text/MultiInput.js
@@ -1,11 +1,15 @@
import React, { useState } from "react";
import PropTypes from "prop-types";
import { useFormikContext, getIn } from "formik";
-
import { FieldLabel } from "../../FieldLabel";
import { SelectField } from "../../SelectField";
+import {
+ createDynamicOverridableComponent,
+ createShowHideComponent,
+ fieldCommonProps,
+} from "../../../utils";
-export default function MultiInput({
+function _MultiInputComponent({
additionLabel,
description,
placeholder,
@@ -13,6 +17,9 @@ export default function MultiInput({
label,
icon,
required,
+ disabled,
+ helpText: helpTextProp,
+ labelIcon: labelIconProp,
}) {
const [options, setOptions] = useState([]);
const { values } = useFormikContext();
@@ -23,14 +30,18 @@ export default function MultiInput({
value: item,
}));
+ const helpText = helpTextProp ?? description;
+ const labelIcon = labelIconProp ?? icon;
+
return (
<>
}
+ label={}
options={serializeValues(getIn(values, fieldPath, []))}
placeholder={placeholder}
required={required}
+ disabled={disabled}
search
multiple
clearable
@@ -47,23 +58,28 @@ export default function MultiInput({
setOptions([{ text: data.value, value: data.value }, ...options]);
}}
/>
- {description && }
+ {helpText && }
>
);
}
-MultiInput.propTypes = {
- fieldPath: PropTypes.string.isRequired,
- label: PropTypes.string.isRequired,
- placeholder: PropTypes.string.isRequired,
+_MultiInputComponent.propTypes = {
+ /**
+ * @deprecated Use `helpText` instead
+ */
description: PropTypes.string.isRequired,
additionLabel: PropTypes.string,
+ /**
+ * @deprecated Use `labelIcon` instead
+ */
icon: PropTypes.string,
- required: PropTypes.bool,
+ ...fieldCommonProps,
};
-MultiInput.defaultProps = {
+_MultiInputComponent.defaultProps = {
additionLabel: undefined,
icon: undefined,
- required: false,
};
+
+export const MultiInputComponent = createShowHideComponent(_MultiInputComponent);
+export const MultiInput = createDynamicOverridableComponent(MultiInputComponent);
diff --git a/src/lib/forms/widgets/text/NumberInput.js b/src/lib/forms/widgets/text/NumberInput.js
index b76913b8..b07d5c75 100644
--- a/src/lib/forms/widgets/text/NumberInput.js
+++ b/src/lib/forms/widgets/text/NumberInput.js
@@ -1,5 +1,10 @@
import React from "react";
-import Input from "./Input";
+import {
+ createDynamicOverridableComponent,
+ createShowHideComponent,
+} from "../../../utils";
+import { InputComponent } from "./Input";
-const NumberInput = (props) => ;
-export default NumberInput;
+const _NumberInputComponent = (props) => ;
+export const NumberInputComponent = createShowHideComponent(_NumberInputComponent);
+export const NumberInput = createDynamicOverridableComponent(NumberInputComponent);
diff --git a/src/lib/forms/widgets/text/RichInput.js b/src/lib/forms/widgets/text/RichInput.js
index 9aa6b0b4..9281b635 100644
--- a/src/lib/forms/widgets/text/RichInput.js
+++ b/src/lib/forms/widgets/text/RichInput.js
@@ -1,38 +1,63 @@
import React, { Component } from "react";
import PropTypes from "prop-types";
-
import { FieldLabel } from "../../FieldLabel";
import { RichInputField } from "../../RichInputField";
+import {
+ createDynamicOverridableComponent,
+ createShowHideComponent,
+ fieldCommonProps,
+} from "../../../utils";
-export default class RichInput extends Component {
+class _RichInputComponent extends Component {
render() {
- const { fieldPath, required, label, icon, description, editorConfig } = this.props;
+ const {
+ fieldPath,
+ required,
+ label,
+ icon,
+ description,
+ editorConfig,
+ disabled,
+ helpText: helpTextProp,
+ labelIcon: labelIconProp,
+ } = this.props;
+
+ const helpText = helpTextProp ?? description;
+ const labelIcon = labelIconProp ?? icon;
+
return (
<>
}
+ label={}
/>
- {description && }
+ {helpText && }
>
);
}
}
-RichInput.propTypes = {
- fieldPath: PropTypes.string.isRequired,
- label: PropTypes.string.isRequired,
- description: PropTypes.string.isRequired,
+_RichInputComponent.propTypes = {
editorConfig: PropTypes.object,
+ /**
+ * @deprecated Use `labelIcon` instead
+ */
icon: PropTypes.string,
- required: PropTypes.bool,
+ /**
+ * @deprecated Use `helpText` instead
+ */
+ description: PropTypes.string.isRequired,
+ ...fieldCommonProps,
};
-RichInput.defaultProps = {
+_RichInputComponent.defaultProps = {
icon: undefined,
editorConfig: {},
- required: false,
};
+
+export const RichInputComponent = createShowHideComponent(_RichInputComponent);
+export const RichInput = createDynamicOverridableComponent(RichInputComponent);
diff --git a/src/lib/forms/widgets/text/TextArea.js b/src/lib/forms/widgets/text/TextArea.js
index 5d0cb855..f8db73fe 100644
--- a/src/lib/forms/widgets/text/TextArea.js
+++ b/src/lib/forms/widgets/text/TextArea.js
@@ -1,12 +1,29 @@
import React, { Component } from "react";
import PropTypes from "prop-types";
-
import { FieldLabel } from "../../FieldLabel";
import { TextAreaField } from "../../TextAreaField";
+import {
+ createShowHideComponent,
+ createDynamicOverridableComponent,
+ fieldCommonProps,
+} from "../../../utils";
-export default class TextArea extends Component {
+class _TextAreaComponent extends Component {
render() {
- const { fieldPath, required, label, icon, description, rows } = this.props;
+ const {
+ fieldPath,
+ required,
+ label,
+ icon,
+ description,
+ rows,
+ disabled,
+ helpText: helpTextProp,
+ labelIcon: labelIconProp,
+ } = this.props;
+
+ const helpText = helpTextProp ?? description;
+ const labelIcon = labelIconProp ?? icon;
return (
<>
@@ -14,26 +31,33 @@ export default class TextArea extends Component {
key={fieldPath}
fieldPath={fieldPath}
required={required}
- label={}
+ disabled={disabled}
rows={rows}
+ label={}
/>
- {description && }
+ {helpText && }
>
);
}
}
-TextArea.propTypes = {
- fieldPath: PropTypes.string.isRequired,
- label: PropTypes.string.isRequired,
- description: PropTypes.string.isRequired,
- icon: PropTypes.string,
- required: PropTypes.bool,
+_TextAreaComponent.propTypes = {
rows: PropTypes.number,
+ /**
+ * @deprecated Use `labelIcon` instead
+ */
+ icon: PropTypes.string,
+ /**
+ * @deprecated Use `helpText` instead
+ */
+ description: PropTypes.string.isRequired,
+ ...fieldCommonProps,
};
-TextArea.defaultProps = {
+_TextAreaComponent.defaultProps = {
icon: undefined,
- required: false,
rows: 3,
};
+
+export const TextAreaComponent = createShowHideComponent(_TextAreaComponent);
+export const TextArea = createDynamicOverridableComponent(TextAreaComponent);
diff --git a/src/lib/forms/widgets/text/index.js b/src/lib/forms/widgets/text/index.js
index b6e59f60..6c1a1c0e 100644
--- a/src/lib/forms/widgets/text/index.js
+++ b/src/lib/forms/widgets/text/index.js
@@ -1,6 +1,6 @@
-export { default as RichInput } from "./RichInput";
-export { default as TextArea } from "./TextArea";
-export { default as Input } from "./Input";
-export { default as MultiInput } from "./MultiInput";
-export { default as NumberInput } from "./NumberInput";
-export { default as BooleanCheckbox } from "./BooleanCheckbox";
+export { RichInput, RichInputComponent } from "./RichInput";
+export { TextArea, TextAreaComponent } from "./TextArea";
+export { Input, InputComponent } from "./Input";
+export { MultiInput, MultiInputComponent } from "./MultiInput";
+export { NumberInput, NumberInputComponent } from "./NumberInput";
+export { BooleanCheckbox, BooleanCheckboxComponent } from "./BooleanCheckbox";
diff --git a/src/lib/utils/fieldComponents.js b/src/lib/utils/fieldComponents.js
new file mode 100644
index 00000000..bb5bf7ec
--- /dev/null
+++ b/src/lib/utils/fieldComponents.js
@@ -0,0 +1,88 @@
+// This file is part of React-Invenio-Forms
+// Copyright (C) 2022-2025 CERN.
+// Copyright (C) 2022 Northwestern University.
+//
+// React-Invenio-Forms is free software; you can redistribute it and/or modify it
+// under the terms of the MIT License; see LICENSE file for more details.
+
+import PropTypes from "prop-types";
+import React from "react";
+import Overridable from "react-overridable";
+
+// Props used for fields that are mandatory for all records
+export const mandatoryFieldCommonProps = {
+ fieldPath: PropTypes.string.isRequired,
+ label: PropTypes.string,
+ labelIcon: PropTypes.string,
+ helpText: PropTypes.string,
+ placeholder: PropTypes.string,
+};
+
+// Also includes props that allow not including a field in the form, which are not applicable
+// to mandatory fields.
+export const fieldCommonProps = {
+ ...mandatoryFieldCommonProps,
+ hidden: PropTypes.bool,
+ disabled: PropTypes.bool,
+ required: PropTypes.bool,
+};
+
+/**
+ * Creates a component that de-renders when the `hidden` prop is `true`.
+ * All props passed to the `ShowHideComponent` are forwarded to the child component except the `hidden` prop.
+ *
+ * @param Component - The child component
+ * @param {string=} id - An optional OverridableID for debugging purposes
+ */
+export const createShowHideComponent = (Component, id) => {
+ const ShowHideComponent = ({ hidden, ...props }) => {
+ if (props.disabled && props.required) {
+ throw new Error(`Cannot make field component ${id} both required and disabled`);
+ }
+ if (hidden) return null;
+ return ;
+ };
+
+ ShowHideComponent.displayName = `ShowHide(${
+ Component.displayName || Component.name
+ })`;
+ ShowHideComponent.propTypes = { ...Component.propTypes };
+ ShowHideComponent.defaultProps = { ...Component.defaultProps };
+ return ShowHideComponent;
+};
+
+/**
+ * Creates an overridable component with show/hide functionality based on the `hidden` prop.
+ * All high-level fields in the deposit form are exported through this function, and this
+ * is the only version of the component that should be exported.
+ *
+ * @param {string} id - The Overridable ID to use
+ * @param Child - The unexported child component
+ */
+export const createCommonDepositFieldComponent = (id, Child) => {
+ const Component = createShowHideComponent(Child);
+ Component.propTypes = Child.propTypes;
+ return Overridable.component(id, Component);
+};
+
+/**
+ * Create a component whos Overridable ID is defined via a prop at runtime rather than pre-assigned.
+ * This is necessary for custom fields, where Python will inject the Overridable ID from the user's
+ * configuration, allowing features like `parametrize` to be applied also to custom fields.
+ */
+export const createDynamicOverridableComponent = (Widget) => {
+ const Component = ({ id, ...props }) => {
+ if (id === undefined) return ;
+
+ return (
+
+
+
+ );
+ };
+
+ Component.propTypes = { ...Widget.propTypes, id: PropTypes.string };
+ Component.defaultProps = { ...Widget.defaultProps, id: undefined };
+ Component.displayName = `DynamicOverridable(${Widget.displayName || Widget.name})`;
+ return Component;
+};
diff --git a/src/lib/utils/index.js b/src/lib/utils/index.js
index 9f213050..07dc3293 100644
--- a/src/lib/utils/index.js
+++ b/src/lib/utils/index.js
@@ -1,2 +1,9 @@
export { humanReadableBytes } from "./humanReadableBytes";
export { dropdownOptionsGenerator } from "./dropdownOptionsGenerator";
+export {
+ createCommonDepositFieldComponent,
+ createDynamicOverridableComponent,
+ createShowHideComponent,
+ fieldCommonProps,
+ mandatoryFieldCommonProps,
+} from "./fieldComponents";