Skip to content

overridable: Implement for custom fields #298

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

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion src/lib/forms/RichInputField.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 =
Expand All @@ -28,6 +29,7 @@ export class RichInputField extends Component {
<Form.Field
id={fieldPath}
required={required}
disabled={disabled}
error={error}
className={className}
>
Expand Down Expand Up @@ -74,6 +76,7 @@ RichInputField.propTypes = {
label: PropTypes.oneOfType([PropTypes.string, PropTypes.object]),
required: PropTypes.bool,
editorConfig: PropTypes.object,
disabled: PropTypes.bool,
};

RichInputField.defaultProps = {
Expand All @@ -83,4 +86,5 @@ RichInputField.defaultProps = {
label: "",
editor: undefined,
editorConfig: undefined,
disabled: false,
};
19 changes: 12 additions & 7 deletions src/lib/forms/SelectField.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
<FormikField
name={fieldPath}
component={this.renderFormField}
fieldPath={fieldPath}
{...uiProps}
/>
<>
<FormikField
name={fieldPath}
component={this.renderFormField}
fieldPath={fieldPath}
{...uiProps}
/>
{helpText && <label className="helptext">{helpText}</label>}
</>
);
}
}
Expand All @@ -113,6 +116,7 @@ SelectField.propTypes = {
onChange: PropTypes.func,
onAddItem: PropTypes.func,
multiple: PropTypes.bool,
helpText: PropTypes.string,
};

SelectField.defaultProps = {
Expand All @@ -123,4 +127,5 @@ SelectField.defaultProps = {
onChange: undefined,
onAddItem: undefined,
multiple: false,
helpText: undefined,
};
43 changes: 28 additions & 15 deletions src/lib/forms/widgets/array/Array.js
Original file line number Diff line number Diff line change
@@ -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,
Expand All @@ -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 (
<ArrayField
key={fieldPath}
fieldPath={fieldPath}
required={required}
helpText={description}
helpText={helpText}
disabled={disabled}
label={<FieldLabel htmlFor={fieldPath} icon={icon} label={label} />}
label={<FieldLabel htmlFor={fieldPath} icon={labelIcon} label={label} />}
addButtonLabel={addButtonLabel}
defaultNewValue={defaultNewValue}
className={className}
Expand All @@ -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);
2 changes: 1 addition & 1 deletion src/lib/forms/widgets/array/index.js
Original file line number Diff line number Diff line change
@@ -1 +1 @@
export { default as Array } from "./Array";
export { Array, ArrayComponent } from "./Array";
48 changes: 36 additions & 12 deletions src/lib/forms/widgets/select/AutocompleteDropdown.js
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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 (
<>
<FieldLabel htmlFor={fieldPath} icon={icon} label={label} />
<FieldLabel htmlFor={fieldPath} icon={labelIcon} label={label} />
<Field name={fieldPath}>
{({ form: { values } }) => {
return (
Expand All @@ -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) => ({
Expand All @@ -58,29 +72,39 @@ export default class AutocompleteDropdown extends Component {
);
}}
</Field>
{description && <label className="helptext">{description}</label>}
</>
);
}
}

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
);
76 changes: 46 additions & 30 deletions src/lib/forms/widgets/select/Dropdown.js
Original file line number Diff line number Diff line change
@@ -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,
Expand All @@ -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 (
<>
<SelectField
fieldPath={fieldPath}
label={<FieldLabel htmlFor={fieldPath} icon={icon} label={label} />}
options={this.serializeOptions(options)}
search={search}
aria-label={label}
multiple={multiple}
placeholder={{
role: "option",
content: placeholder,
}}
clearable={clearable}
required={required}
optimized
defaultValue={multiple ? [] : ""}
/>
{description && <label className="helptext">{description}</label>}
</>
<SelectField
fieldPath={fieldPath}
label={<FieldLabel htmlFor={fieldPath} icon={labelIcon} label={label} />}
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);
7 changes: 5 additions & 2 deletions src/lib/forms/widgets/select/index.js
Original file line number Diff line number Diff line change
@@ -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";
Loading