Skip to content

Commit

Permalink
Add option for custom widgets in schemawidget.
Browse files Browse the repository at this point in the history
  • Loading branch information
robgietema committed Nov 13, 2024
1 parent be53373 commit dc68a64
Show file tree
Hide file tree
Showing 3 changed files with 65 additions and 59 deletions.
65 changes: 37 additions & 28 deletions packages/volto/src/components/manage/Form/Field.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,41 +13,46 @@ const MODE_HIDDEN = 'hidden'; //hidden mode. If mode is hidden, field is not ren
/**
* Get default widget
* @method getViewDefault
* @param {object} widgets Widgets config
* @returns {string} Widget component.
*/
const getWidgetDefault = () => config.widgets.default;
const getWidgetDefault = (widgets) => widgets.default;

/**
* Get widget by field's `id` attribute
* @method getWidgetById
* @param {object} widgets Widgets config
* @param {string} id Id
* @returns {string} Widget component.
*/
const getWidgetByFieldId = (id) => config.widgets.id[id] || null;
const getWidgetByFieldId = (widgets, id) => widgets.id[id] || null;

/**
* Get widget by factory attribute
* @method getWidgetByFactory
* @param {object} widgets Widgets config
* @param {string} id Id
* @returns {string} Widget component.
*/
const getWidgetByFactory = (factory) =>
config.widgets.factory?.[factory] || null;
const getWidgetByFactory = (widgets, factory) =>
widgets.factory?.[factory] || null;

/**
* Get widget by field's `widget` attribute
* @method getWidgetByName
* @param {object} widgets Widgets config
* @param {string} widget Widget
* @returns {string} Widget component.
*/
const getWidgetByName = (widget) =>
const getWidgetByName = (widgets, widget) =>
typeof widget === 'string'
? config.widgets.widget[widget] || getWidgetDefault()
? widgets.widget[widget] || getWidgetDefault(widgets)
: null;

/**
* Get widget by tagged values
* @param {object} widgetOptions
* @param {object} widgets Widgets config
* @returns {string} Widget component.
*
Expand All @@ -59,13 +64,14 @@ directives.widget(
})
*/
const getWidgetFromTaggedValues = (widgetOptions) =>
const getWidgetFromTaggedValues = (widgets, widgetOptions) =>
typeof widgetOptions?.frontendOptions?.widget === 'string'
? config.widgets.widget[widgetOptions.frontendOptions.widget]
? widgets.widget[widgetOptions.frontendOptions.widget]
: null;

/**
* Get widget props from tagged values
* @param {object} widgets Widgets config
* @param {object} widgetOptions
* @returns {string} Widget component.
*
Expand All @@ -78,33 +84,33 @@ directives.widget(
})
*/
const getWidgetPropsFromTaggedValues = (widgetOptions) =>
const getWidgetPropsFromTaggedValues = (widgets, widgetOptions) =>
typeof widgetOptions?.frontendOptions?.widgetProps === 'object'
? widgetOptions.frontendOptions.widgetProps
: null;

/**
* Get widget by field's `vocabulary` attribute
* @method getWidgetByVocabulary
* @param {object} widgets Widgets config
* @param {string} vocabulary Widget
* @returns {string} Widget component.
*/
const getWidgetByVocabulary = (vocabulary) =>
const getWidgetByVocabulary = (widgets, vocabulary) =>
vocabulary && vocabulary['@id']
? config.widgets.vocabulary[
vocabulary['@id'].replace(/^.*\/@vocabularies\//, '')
]
? widgets.vocabulary[vocabulary['@id'].replace(/^.*\/@vocabularies\//, '')]
: null;

/**
* Get widget by field's hints `vocabulary` attribute in widgetOptions
* @method getWidgetByVocabularyFromHint
* @param {object} widgets Widgets config
* @param {string} props Widget props
* @returns {string} Widget component.
*/
const getWidgetByVocabularyFromHint = (props) =>
const getWidgetByVocabularyFromHint = (widgets, props) =>
props.widgetOptions && props.widgetOptions.vocabulary
? config.widgets.vocabulary[
? widgets.vocabulary[
props.widgetOptions.vocabulary['@id'].replace(
/^.*\/@vocabularies\//,
'',
Expand All @@ -115,19 +121,20 @@ const getWidgetByVocabularyFromHint = (props) =>
/**
* Get widget by field's `choices` attribute
* @method getWidgetByChoices
* @param {object} widgets Widgets config
* @param {string} choices Widget
* @returns {string} Widget component.
*/
const getWidgetByChoices = (props) => {
const getWidgetByChoices = (widgets, props) => {
if (props.choices) {
return config.widgets.choices;
return widgets.choices;
}

if (props.vocabulary) {
// If vocabulary exists, then it means it's a choice field in disguise with
// no widget specified that probably contains a string then we force it
// to be a select widget instead
return config.widgets.choices;
return widgets.choices;
}

return null;
Expand All @@ -136,10 +143,11 @@ const getWidgetByChoices = (props) => {
/**
* Get widget by field's `type` attribute
* @method getWidgetByType
* @param {object} widgets Widgets config
* @param {string} type Type
* @returns {string} Widget component.
*/
const getWidgetByType = (type) => config.widgets.type[type] || null;
const getWidgetByType = (widgets, type) => widgets.type[type] || null;

/**
* Field component class.
Expand All @@ -148,16 +156,17 @@ const getWidgetByType = (type) => config.widgets.type[type] || null;
* @returns {string} Markup of the component.
*/
const UnconnectedField = (props, { intl }) => {
const widgets = props.widgets || config.widgets;
const Widget =
getWidgetByFieldId(props.id) ||
getWidgetFromTaggedValues(props.widgetOptions) ||
getWidgetByName(props.widget) ||
getWidgetByChoices(props) ||
getWidgetByVocabulary(props.vocabulary) ||
getWidgetByVocabularyFromHint(props) ||
getWidgetByFactory(props.factory) ||
getWidgetByType(props.type) ||
getWidgetDefault();
getWidgetByFieldId(widgets, props.id) ||
getWidgetFromTaggedValues(widgets, props.widgetOptions) ||
getWidgetByName(widgets, props.widget) ||
getWidgetByChoices(widgets, props) ||
getWidgetByVocabulary(widgets, props.vocabulary) ||
getWidgetByVocabularyFromHint(widgets, props) ||
getWidgetByFactory(widgets, props.factory) ||
getWidgetByType(widgets, props.type) ||
getWidgetDefault(widgets);

if (props.mode === MODE_HIDDEN) {
return null;
Expand Down
57 changes: 26 additions & 31 deletions packages/volto/src/components/manage/Form/Form.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ class Form extends Component {
definitions: PropTypes.objectOf(PropTypes.any),
required: PropTypes.arrayOf(PropTypes.string),
}),
widgets: PropTypes.objectOf(PropTypes.any),
formData: PropTypes.objectOf(PropTypes.any),
globalData: PropTypes.objectOf(PropTypes.any),
metadataFieldsets: PropTypes.arrayOf(PropTypes.string),
Expand Down Expand Up @@ -123,6 +124,7 @@ class Form extends Component {
*/
static defaultProps = {
formData: null,
widgets: null,
onSubmit: null,
onCancel: null,
submitLabel: null,
Expand Down Expand Up @@ -278,22 +280,19 @@ class Form extends Component {
selected: null,
});
}
if (requestError) {

if (requestError && prevProps.requestError !== requestError) {
errors =
FormValidation.giveServerErrorsToCorrespondingFields(requestError);
if (
!isEqual(prevProps.requestError, requestError) ||
!isEqual(this.state.errors, errors)
) {
activeIndex = FormValidation.showFirstTabWithErrors({
errors,
schema: this.props.schema,
});
this.setState({
errors,
activeIndex,
});
}
activeIndex = FormValidation.showFirstTabWithErrors({
errors,
schema: this.props.schema,
});

this.setState({
errors,
activeIndex,
});
}

if (this.props.onChangeFormData) {
Expand Down Expand Up @@ -570,11 +569,13 @@ class Form extends Component {
}
});
}

if (keys(errors).length > 0 || keys(blocksErrors).length > 0) {
const activeIndex = FormValidation.showFirstTabWithErrors({
errors,
schema: this.props.schema,
});

this.setState({
errors: {
...errors,
Expand All @@ -585,23 +586,14 @@ class Form extends Component {

if (keys(errors).length > 0) {
// Changes the focus to the metadata tab in the sidebar if error
toast.error(
<Toast
error
title={this.props.intl.formatMessage(messages.error)}
content={
<ul>
{Object.keys(errors).map((err, index) => (
<li key={index}>
<strong>
{this.props.schema.properties[err].title || err}:
</strong>{' '}
{errors[err]}
</li>
))}
</ul>
}
/>,
Object.keys(errors).forEach((err) =>
toast.error(
<Toast
error
title={this.props.schema.properties[err].title || err}
content={errors[err].join(', ')}
/>,
),
);
this.props.setSidebarTab(0);
} else if (keys(blocksErrors).length > 0) {
Expand Down Expand Up @@ -728,6 +720,7 @@ class Form extends Component {
const schema = this.removeBlocksLayoutFields(originalSchema);
const Container =
config.getComponent({ name: 'Container' }).component || SemanticContainer;

return this.props.visual ? (
// Removing this from SSR is important, since react-beautiful-dnd supports SSR,
// but draftJS don't like it much and the hydration gets messed up
Expand Down Expand Up @@ -933,6 +926,7 @@ class Form extends Component {
),
...map(item.fields, (field, index) => (
<Field
widgets={this.props.widgets}
{...schema.properties[field]}
id={field}
formData={formData}
Expand Down Expand Up @@ -988,6 +982,7 @@ class Form extends Component {
)}
{map(schema.fieldsets[0].fields, (field) => (
<Field
widgets={this.props.widgets}
{...schema.properties[field]}
id={field}
value={formData?.[field]}
Expand Down
2 changes: 2 additions & 0 deletions packages/volto/src/components/manage/Widgets/SchemaWidget.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -1494,6 +1494,7 @@ class SchemaWidget extends Component {
{...this.props.value.properties[field]}
id={field}
required={this.props.value.required.indexOf(field) !== -1}
widgets={this.props.widgets}
onEdit={this.onShowEditField}
draggable={false}
isDisabled={true}
Expand Down Expand Up @@ -1532,6 +1533,7 @@ class SchemaWidget extends Component {
{...this.props.value.properties[field]}
id={field}
required={this.props.value.required.indexOf(field) !== -1}
widgets={this.props.widgets}
onEdit={this.onShowEditField}
draggable={true}
isDisabled={false}
Expand Down

0 comments on commit dc68a64

Please sign in to comment.