From cafad1c570195023a82007e31207c78f97ca4177 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katja=20S=C3=BCss?= Date: Tue, 11 Apr 2023 17:26:38 +0200 Subject: [PATCH] Support RelationList field with StaticCatalogVocabulary and SelectWidget. (#4614) Co-authored-by: Steve Piercy --- docs/source/recipes/widget.md | 102 +++++++++++++++++- news/4614.feature | 1 + src/components/manage/Widgets/SelectUtils.js | 2 +- .../manage/Widgets/SelectWidget.jsx | 2 +- src/config/Widgets.jsx | 1 + 5 files changed, 104 insertions(+), 4 deletions(-) create mode 100644 news/4614.feature diff --git a/docs/source/recipes/widget.md b/docs/source/recipes/widget.md index ebb232d0d5..6b2e81d185 100644 --- a/docs/source/recipes/widget.md +++ b/docs/source/recipes/widget.md @@ -146,14 +146,112 @@ const applyConfig = (config) => { Based on this setup, Volto will render this field with the `TokenWidget`. -```{seealso} -See [storybook](https://6.dev-docs.plone.org/storybook) with available widgets. +(widget-relation-field-label)= + +## Relation fields + +A relation field is either a single relation field to hold at most one content object, `RelationChoice`, or a multi relation field, `RelationList`, that can hold more than one content object. + +Relation fields can be edited and rendered with the `Select` widget. +The restriction on content types, workflow states, and so on can be done with a `StaticCatalogVocabulary`. + +There are other vocabulary types and other widgets, including the `ObjectBrowser` widget. + +(widget-relation-field-single-label)= + +### Single relation field + +Relation field (`RelationChoice`) with a named `StaticCatalogVocabulary` and `Select` widget: + +```python +relationchoice_field_named_staticcatalogvocabulary = RelationChoice( + title="RelationChoice – named StaticCatalogVocabulary – Select widget", + description="field/relation: relationchoice_field_named_staticcatalogvocabulary", + vocabulary="relationchoice_field_named_staticcatalogvocabulary", + required=False, +) +directives.widget( + "relationchoice_field_named_staticcatalogvocabulary", + frontendOptions={ + "widget": "select", + }, +) ``` +It is recommended to define the vocabulary as a named `StaticCatalogVocabulary` with the field/relation name as its name. +This allows the {guilabel}`relations` control panel to respect the defined restrictions to potential relation targets. + +{file}`vocabularies.py` +```python +from plone.app.vocabularies.catalog import StaticCatalogVocabulary +from zope.interface import provider +from zope.schema.interfaces import IVocabularyFactory + +@provider(IVocabularyFactory) +def ExamplesVocabularyFactory(context=None): + return StaticCatalogVocabulary( + { + "portal_type": ["example"], + "review_state": "published", + "sort_on": "sortable_title", + } + ) +``` + +{file}`configure.zcml` +```xml + +``` + +The `Select` widget is currently the default for `RelationChoice` fields with vocabulary. +Therefore the directive can be omitted. + +```python +relationchoice_field_named_staticcatalogvocabulary = RelationChoice( + title="RelationChoice – named StaticCatalogVocabulary – Select widget", + description="field/relation: relationchoice_field_named_staticcatalogvocabulary", + vocabulary="relationchoice_field_named_staticcatalogvocabulary", + required=False, +) +``` + +(widget-relation-field-multi-label)= + +### Multi relation field + +Multi relation field (`RelationList`) with a named `StaticCatalogVocabulary`and `Select` widget: + +```python +relationlist_field_named_staticcatalogvocabulary = RelationList( + title="RelationList – named StaticCatalogVocabulary – Select widget", + description="field/relation: relationlist_field_named_staticcatalogvocabulary", + value_type=RelationChoice( + vocabulary="relationlist_field_named_staticcatalogvocabulary", + ), + required=False, +) +directives.widget( + "relationlist_field_named_staticcatalogvocabulary", + frontendOptions={ + "widget": "select", + }, +) +``` + + ## Widget `isDisabled` Props We can disable the input of a widget by passing props `isDisabled: true`. + +## Available widgets + +See [Storybook](https://6.docs.plone.org/storybook) with available widgets. + + ## Write a new widget ```{note} diff --git a/news/4614.feature b/news/4614.feature new file mode 100644 index 0000000000..286f2fe464 --- /dev/null +++ b/news/4614.feature @@ -0,0 +1 @@ +Support RelationList field with named StaticCatalogVocabulary and SelectWidget. @ksuess \ No newline at end of file diff --git a/src/components/manage/Widgets/SelectUtils.js b/src/components/manage/Widgets/SelectUtils.js index eec6f2f82b..7d0129f3fa 100644 --- a/src/components/manage/Widgets/SelectUtils.js +++ b/src/components/manage/Widgets/SelectUtils.js @@ -54,7 +54,7 @@ export function normalizeSingleSelectOption(value, intl) { throw new Error(`Unknown value type of select widget: ${value}`); } - const token = value.token ?? value.value ?? 'no-value'; + const token = value.token ?? value.value ?? value.UID ?? 'no-value'; const label = (value.title && value.title !== 'None' ? value.title : undefined) ?? value.label ?? diff --git a/src/components/manage/Widgets/SelectWidget.jsx b/src/components/manage/Widgets/SelectWidget.jsx index 05bd609602..0045a2568f 100644 --- a/src/components/manage/Widgets/SelectWidget.jsx +++ b/src/components/manage/Widgets/SelectWidget.jsx @@ -202,7 +202,7 @@ class SelectWidget extends Component { const isMulti = this.props.isMulti ? this.props.isMulti - : id === 'roles' || id === 'groups'; + : id === 'roles' || id === 'groups' || this.props.type === 'array'; return ( diff --git a/src/config/Widgets.jsx b/src/config/Widgets.jsx index 9dcf7f7cb2..c111cf75ae 100644 --- a/src/config/Widgets.jsx +++ b/src/config/Widgets.jsx @@ -98,6 +98,7 @@ export const widgetMapping = { select_querystring_field: SelectMetadataWidget, autocomplete: SelectAutoComplete, color_picker: ColorPickerWidget, + select: SelectWidget, }, vocabulary: { 'plone.app.vocabularies.Catalog': ObjectBrowserWidget,