From 7fe372c75a04a1e0785b1f33bd225a8144b8a88d Mon Sep 17 00:00:00 2001 From: Pierre Dureau <31905-pdureau@users.noreply.drupalcode.org> Date: Thu, 8 Aug 2024 12:05:32 +0000 Subject: [PATCH] Issue #3461277 by pdureau, Christian.wiedemann: Sources don't support required - #title missing --- .../src/ComponentConverter.php | 17 ++++++++ .../Commands/UiPatternsLegacyCommands.php | 42 ++++++++++--------- .../ui_patterns_legacy.services.yml | 1 + src/ComponentPluginManager.php | 29 +++++++++---- src/Element/ComponentPropForm.php | 21 ++++++++++ .../UiPatterns/Source/AttributesWidget.php | 11 ++--- .../UiPatterns/Source/CheckboxWidget.php | 10 ++--- .../UiPatterns/Source/CheckboxesWidget.php | 13 +++--- .../UiPatterns/Source/EntityLinksSource.php | 8 ++-- .../UiPatterns/Source/ListTextareaWidget.php | 13 +++--- src/Plugin/UiPatterns/Source/MenuSource.php | 10 ++--- src/Plugin/UiPatterns/Source/NumberWidget.php | 15 +++---- src/Plugin/UiPatterns/Source/PathSource.php | 12 +++--- src/Plugin/UiPatterns/Source/SelectWidget.php | 9 ++-- .../UiPatterns/Source/TextfieldWidget.php | 18 ++++---- src/Plugin/UiPatterns/Source/TokenSource.php | 16 +++---- src/Plugin/UiPatterns/Source/UrlWidget.php | 13 +++--- .../UiPatterns/Source/WysiwygWidget.php | 4 +- src/PropTypePluginBase.php | 3 ++ src/SourcePluginBase.php | 19 ++++++++- .../src/Plugin/UiPatterns/Source/Foo.php | 13 +++--- 21 files changed, 191 insertions(+), 106 deletions(-) diff --git a/modules/ui_patterns_legacy/src/ComponentConverter.php b/modules/ui_patterns_legacy/src/ComponentConverter.php index 05107f506..eb63c7578 100644 --- a/modules/ui_patterns_legacy/src/ComponentConverter.php +++ b/modules/ui_patterns_legacy/src/ComponentConverter.php @@ -69,6 +69,10 @@ class ComponentConverter { "type" => 'object', "properties" => $this->getPropsFromSettings($source['settings']), ]; + $required_props = $this->getRequiredPropsFromSettings($source['settings']); + if (!empty($required_props)) { + $target['props']['required'] = $required_props; + } } $extractor = new StoryExtractor($this->componentPluginManager); $extractor->setExtension($this->extension); @@ -164,6 +168,19 @@ class ComponentConverter { return $props; } + /** + * Get required props from UI Patterns 1.x settings. + */ + private function getRequiredPropsFromSettings(array $settings): array { + $props = []; + foreach ($settings as $setting_id => $setting) { + if (\array_key_exists('required', $setting) && $setting["required"]) { + $props[] = $setting_id; + } + } + return $props; + } + /** * A small helper to convert a property. */ diff --git a/modules/ui_patterns_legacy/src/Drush/Commands/UiPatternsLegacyCommands.php b/modules/ui_patterns_legacy/src/Drush/Commands/UiPatternsLegacyCommands.php index fd9d60fda..f8ac162aa 100644 --- a/modules/ui_patterns_legacy/src/Drush/Commands/UiPatternsLegacyCommands.php +++ b/modules/ui_patterns_legacy/src/Drush/Commands/UiPatternsLegacyCommands.php @@ -1,5 +1,7 @@ <?php +declare(strict_types=1); + namespace Drupal\ui_patterns_legacy\Drush\Commands; use Drupal\Component\Plugin\Discovery\CachedDiscoveryInterface; @@ -7,46 +9,51 @@ use Drupal\ui_patterns_legacy\ComponentConverter; use Drupal\ui_patterns_legacy\ComponentDiscovery; use Drupal\ui_patterns_legacy\ComponentWriter; use Drush\Attributes as CLI; +use Drush\Commands\AutowireTrait; use Drush\Commands\DrushCommands; -use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Component\DependencyInjection\Attribute\Autowire; use Symfony\Component\Finder\Finder; /** * Drush commands of UI Patterns Legacy module. */ final class UiPatternsLegacyCommands extends DrushCommands { + use AutowireTrait; /** * {@inheritdoc} */ public function __construct( + #[Autowire(service: 'ui_patterns_legacy.component_converter')] private readonly ComponentConverter $converter, + #[Autowire(service: 'ui_patterns_legacy.discovery')] private readonly ComponentDiscovery $discovery, + #[Autowire(service: 'ui_patterns_legacy.component_writer')] private readonly ComponentWriter $writer, + #[Autowire(service: 'plugin.manager.sdc')] private readonly CachedDiscoveryInterface $componentsManager, ) { parent::__construct(); } - /** - * {@inheritdoc} - */ - public static function create(ContainerInterface $container): static { - return new static( - $container->get('ui_patterns_legacy.component_converter'), - $container->get('ui_patterns_legacy.discovery'), - $container->get('ui_patterns_legacy.component_writer'), - $container->get('plugin.manager.sdc') - ); - } - /** * Migrate components from UI Patterns 1.x to UI Patterns 2.x. */ #[CLI\Command(name: 'ui-patterns:migrate', aliases: ['upm'])] #[CLI\Argument(name: 'extension', description: 'Module or theme machine name.')] - #[CLI\Usage(name: 'ui-patterns:migrate my_theme', description: 'Migrate components, replace dependencies and API calls.')] + #[CLI\Usage(name: 'ui-patterns:migrate my_theme', description: 'Convert components, replace dependencies and API calls.')] public function migrate(string $extension): void { + $this->convertComponents($extension); + $extension_path = $this->discovery->getExtensionPath($extension); + $this->replaceDeprecatedCalls($extension_path); + $this->changeInfoFile($extension, $extension_path); + $this->changeComposerFile($extension_path); + } + + /** + * Convert components. + */ + protected function convertComponents(string $extension): void { $legacy_definitions = $this->discovery->discover($extension); $extension_path = $this->discovery->getExtensionPath($extension); $this->converter->setExtension($extension); @@ -72,13 +79,9 @@ final class UiPatternsLegacyCommands extends DrushCommands { } $errors = $this->converter->validate($component); foreach ($errors as $error) { - $this->io()->error($error); + $this->logger()->error($error); } } - $extension_path = $this->discovery->getExtensionPath($extension); - $this->replaceDeprecatedCalls($extension_path); - $this->changeInfoFile($extension, $extension_path); - $this->changeComposerFile($extension_path); } /** @@ -90,6 +93,7 @@ final class UiPatternsLegacyCommands extends DrushCommands { */ protected function replaceDeprecatedCalls(string $extension_path): void { $finder = new Finder(); + $patterns = ['*.php', '*.inc', '*.module', '*.theme']; $finder->files()->name($patterns)->in($extension_path); foreach ($finder as $file) { diff --git a/modules/ui_patterns_legacy/ui_patterns_legacy.services.yml b/modules/ui_patterns_legacy/ui_patterns_legacy.services.yml index dd33f279a..6a8191089 100644 --- a/modules/ui_patterns_legacy/ui_patterns_legacy.services.yml +++ b/modules/ui_patterns_legacy/ui_patterns_legacy.services.yml @@ -13,6 +13,7 @@ services: class: Drupal\ui_patterns_legacy\ComponentConverter arguments: - "@renderer" + - "@plugin.manager.sdc" ui_patterns_legacy.component_writer: class: Drupal\ui_patterns_legacy\ComponentWriter ui_patterns_legacy.component_element_alter: diff --git a/src/ComponentPluginManager.php b/src/ComponentPluginManager.php index 768b7cc2f..17d0577e7 100644 --- a/src/ComponentPluginManager.php +++ b/src/ComponentPluginManager.php @@ -73,6 +73,7 @@ class ComponentPluginManager extends SdcPluginManager implements CategorizingPlu // Adding custom logic. $fallback_prop_type_id = $this->propTypePluginManager->getFallbackPluginId(""); $definition = $this->alterSlots($definition); + $definition = $this->annotateSlots($definition); $definition = $this->annotateProps($definition, $fallback_prop_type_id); return $definition; } @@ -91,6 +92,21 @@ class ComponentPluginManager extends SdcPluginManager implements CategorizingPlu return $definition; } + /** + * Annotate each slot in a component definition. + */ + protected function annotateSlots(array $definition): array { + if (empty($definition['slots'])) { + return $definition; + } + $slot_prop_type = $this->propTypePluginManager->createInstance('slot', []); + foreach ($definition['slots'] as $slot_id => $slot) { + $slot['ui_patterns']['type_definition'] = $slot_prop_type; + $definition['slots'][$slot_id] = $slot; + } + return $definition; + } + /** * Annotate each prop in a component definition. * @@ -98,6 +114,12 @@ class ComponentPluginManager extends SdcPluginManager implements CategorizingPlu * We add a 'ui_patterns' object in each prop schema of the definition. */ protected function annotateProps(array $definition, string $fallback_prop_type_id): array { + // In JSON schema, 'required' is out of the prop definition. + if (isset($definition['props']['required'])) { + foreach ($definition['props']['required'] as $prop_id) { + $definition['props']['properties'][$prop_id]['ui_patterns']['required'] = TRUE; + } + } if (!isset($definition['props']['properties'])) { return $definition; } @@ -107,13 +129,6 @@ class ComponentPluginManager extends SdcPluginManager implements CategorizingPlu foreach ($definition['props']['properties'] as $prop_id => $prop) { $definition['props']['properties'][$prop_id] = $this->annotateProp($prop_id, $prop, $fallback_prop_type_id); } - $slot_prop_type = $this->propTypePluginManager->createInstance('slot', []); - if (!empty($definition['slots'])) { - foreach ($definition['slots'] as $slot_id => $slot) { - $slot['ui_patterns']['type_definition'] = $slot_prop_type; - $definition['slots'][$slot_id] = $slot; - } - } return $definition; } diff --git a/src/Element/ComponentPropForm.php b/src/Element/ComponentPropForm.php index 767510be8..e21838ff3 100644 --- a/src/Element/ComponentPropForm.php +++ b/src/Element/ComponentPropForm.php @@ -93,6 +93,27 @@ class ComponentPropForm extends ComponentFormBase { 'source' => $source_form, ]; $element['#attributes']['style'] = 'position: relative;'; + $element = static::addRequired($element, $prop_id); + return $element; + } + + /** + * Add required visual clue to the fieldset. + * + * The proper required control is managed by SourcePluginBase::addRequired() + * so the visual clue is present whether or not the control is done by the + * source plugin. This is feature, not a bug. + */ + protected static function addRequired(array $element, string $prop_id): array { + $component = static::getComponent($element); + if (!isset($component->metadata->schema["required"])) { + return $element; + } + $required_props = $component->metadata->schema["required"]; + if (!in_array($prop_id, $required_props)) { + return $element; + } + $element["#required"] = TRUE; return $element; } diff --git a/src/Plugin/UiPatterns/Source/AttributesWidget.php b/src/Plugin/UiPatterns/Source/AttributesWidget.php index d78ad4aab..6e0650d3b 100644 --- a/src/Plugin/UiPatterns/Source/AttributesWidget.php +++ b/src/Plugin/UiPatterns/Source/AttributesWidget.php @@ -39,11 +39,11 @@ class AttributesWidget extends SourcePluginBase { * {@inheritdoc} */ public function settingsForm(array $form, FormStateInterface $form_state): array { - $build = []; + $form = parent::settingsForm($form, $form_state); // Attributes are associative arrays, but this source plugin is storing // them as string in config. // It would be better to use something else than a textfield one day. - $build['value'] = [ + $form['value'] = [ '#type' => 'textfield', '#default_value' => $this->getSetting('value'), ]; @@ -52,9 +52,10 @@ class AttributesWidget extends SourcePluginBase { // See https://www.w3.org/TR/2011/WD-html5-20110525/syntax.html#attributes-0 $forbidden_characters = "<>&/`"; $pattern = "^(([^" . $forbidden_characters . "]+)=[\"'].+[\"']\s*)*?$"; - $build['value']['#pattern'] = $pattern; - $build['value']['#description'] = $this->t("Values must be present and quoted."); - return $build; + $form['value']['#pattern'] = $pattern; + $form['value']['#description'] = $this->t("Values must be present and quoted."); + $this->addRequired($form['value']); + return $form; } /** diff --git a/src/Plugin/UiPatterns/Source/CheckboxWidget.php b/src/Plugin/UiPatterns/Source/CheckboxWidget.php index 42f6bceb4..84fe1e472 100644 --- a/src/Plugin/UiPatterns/Source/CheckboxWidget.php +++ b/src/Plugin/UiPatterns/Source/CheckboxWidget.php @@ -32,12 +32,12 @@ class CheckboxWidget extends SourcePluginPropValue { * {@inheritdoc} */ public function settingsForm(array $form, FormStateInterface $form_state): array { - return [ - 'value' => [ - '#type' => 'checkbox', - '#default_value' => $this->getSetting('value'), - ], + $form = parent::settingsForm($form, $form_state); + $form['value'] = [ + '#type' => 'checkbox', + '#default_value' => $this->getSetting('value'), ]; + return $form; } } diff --git a/src/Plugin/UiPatterns/Source/CheckboxesWidget.php b/src/Plugin/UiPatterns/Source/CheckboxesWidget.php index 6fb930be5..8c5527466 100644 --- a/src/Plugin/UiPatterns/Source/CheckboxesWidget.php +++ b/src/Plugin/UiPatterns/Source/CheckboxesWidget.php @@ -34,13 +34,14 @@ class CheckboxesWidget extends SourcePluginPropValue { * {@inheritdoc} */ public function settingsForm(array $form, FormStateInterface $form_state): array { - return [ - 'value' => [ - '#type' => 'checkboxes', - '#default_value' => $this->getSetting('value') ?? [], - "#options" => $this->getOptions(), - ], + $form = parent::settingsForm($form, $form_state); + $form['value'] = [ + '#type' => 'checkboxes', + '#default_value' => $this->getSetting('value') ?? [], + "#options" => $this->getOptions(), ]; + $this->addRequired($form['value']); + return $form; } /** diff --git a/src/Plugin/UiPatterns/Source/EntityLinksSource.php b/src/Plugin/UiPatterns/Source/EntityLinksSource.php index 242c3ceff..c28dffeab 100644 --- a/src/Plugin/UiPatterns/Source/EntityLinksSource.php +++ b/src/Plugin/UiPatterns/Source/EntityLinksSource.php @@ -109,12 +109,12 @@ class EntityLinksSource extends SourcePluginBase { * {@inheritdoc} */ public function settingsForm(array $form, FormStateInterface $form_state): array { - $build = []; + $form = parent::settingsForm($form, $form_state); $entity_links = $this->getEntityLinkTemplates(); $link_templates_options = array_keys($entity_links); $link_templates_options = array_combine($link_templates_options, $link_templates_options); asort($link_templates_options); - $build["template"] = [ + $form["template"] = [ '#type' => 'select', '#title' => $this->t("Select"), '#options' => $link_templates_options, @@ -122,12 +122,12 @@ class EntityLinksSource extends SourcePluginBase { "#empty_option" => $this->t("- Select -"), '#empty_value' => '', ]; - $build['absolute'] = [ + $form['absolute'] = [ '#type' => 'checkbox', '#title' => $this->t('Absolute URL'), '#default_value' => $this->isAbsoluteUrl(), ]; - return $build; + return $form; } /** diff --git a/src/Plugin/UiPatterns/Source/ListTextareaWidget.php b/src/Plugin/UiPatterns/Source/ListTextareaWidget.php index 84ac17dfa..a18381ed6 100644 --- a/src/Plugin/UiPatterns/Source/ListTextareaWidget.php +++ b/src/Plugin/UiPatterns/Source/ListTextareaWidget.php @@ -32,17 +32,18 @@ class ListTextareaWidget extends SourcePluginBase { * {@inheritdoc} */ public function settingsForm(array $form, FormStateInterface $form_state): array { + $form = parent::settingsForm($form, $form_state); $items = $this->getSetting('value'); if (is_array($items)) { $items = implode("\r", $items); } - return [ - 'value' => [ - '#type' => 'textarea', - '#default_value' => $items, - "#description" => $this->t("One item by line"), - ], + $form['value'] = [ + '#type' => 'textarea', + '#default_value' => $items, + "#description" => $this->t("One item by line"), ]; + $this->addRequired($form['value']); + return $form; } } diff --git a/src/Plugin/UiPatterns/Source/MenuSource.php b/src/Plugin/UiPatterns/Source/MenuSource.php index 3ddee4947..2dfd73e5b 100644 --- a/src/Plugin/UiPatterns/Source/MenuSource.php +++ b/src/Plugin/UiPatterns/Source/MenuSource.php @@ -100,8 +100,8 @@ class MenuSource extends SourcePluginBase { * {@inheritdoc} */ public function settingsForm(array $form, FormStateInterface $form_state): array { - $build = []; - $build["menu"] = [ + $form = parent::settingsForm($form, $form_state); + $form["menu"] = [ '#type' => 'select', '#title' => $this->t("Menu"), '#options' => $this->getMenuList(), @@ -109,14 +109,14 @@ class MenuSource extends SourcePluginBase { ]; $options = range(0, $this->menuLinkTree->maxDepth()); unset($options[0]); - $build['level'] = [ + $form['level'] = [ '#type' => 'select', '#title' => $this->t('Initial visibility level'), '#default_value' => $this->getSetting('level'), '#options' => $options, ]; $options[0] = $this->t('Unlimited'); - $build['depth'] = [ + $form['depth'] = [ '#type' => 'select', '#title' => $this->t('Number of levels to display'), '#default_value' => $this->getSetting('depth'), @@ -125,7 +125,7 @@ class MenuSource extends SourcePluginBase { 'This maximum number includes the initial level and the final display is dependant of the component template.' ), ]; - return $build; + return $form; } /** diff --git a/src/Plugin/UiPatterns/Source/NumberWidget.php b/src/Plugin/UiPatterns/Source/NumberWidget.php index 2d8ce1499..cb81b7876 100644 --- a/src/Plugin/UiPatterns/Source/NumberWidget.php +++ b/src/Plugin/UiPatterns/Source/NumberWidget.php @@ -37,29 +37,30 @@ class NumberWidget extends SourcePluginPropValue { * {@inheritdoc} */ public function settingsForm(array $form, FormStateInterface $form_state): array { - $build = []; - $build['value'] = [ + $form = parent::settingsForm($form, $form_state); + $form['value'] = [ '#type' => 'number', '#default_value' => $this->getSetting('value'), '#step' => 0.01, ]; if ($this->propDefinition["type"] === "integer") { - $build['value']['#step'] = 1; + $form['value']['#step'] = 1; } // Because of SDC's ComponentMetadata::parseSchemaInfo() which is adding // "object" type to all props to "allows deferring rendering in Twig to the // render pipeline". Remove it as soon as this weird mechanism is removed // from SDC. if (in_array("integer", $this->propDefinition["type"])) { - $build['value']['#step'] = 1; + $form['value']['#step'] = 1; } if (isset($this->propDefinition["minimum"])) { - $build['value']['#min'] = $this->propDefinition["minimum"]; + $form['value']['#min'] = $this->propDefinition["minimum"]; } if (isset($this->propDefinition["maximum"])) { - $build['value']['#max'] = $this->propDefinition["maximum"]; + $form['value']['#max'] = $this->propDefinition["maximum"]; } - return $build; + $this->addRequired($form['value']); + return $form; } } diff --git a/src/Plugin/UiPatterns/Source/PathSource.php b/src/Plugin/UiPatterns/Source/PathSource.php index e818f026b..9724cb52e 100644 --- a/src/Plugin/UiPatterns/Source/PathSource.php +++ b/src/Plugin/UiPatterns/Source/PathSource.php @@ -48,15 +48,15 @@ class PathSource extends SourcePluginBase { * {@inheritdoc} */ public function settingsForm(array $form, FormStateInterface $form_state): array { + $form = parent::settingsForm($form, $form_state); $value = $this->getSetting('value'); $value = $this->getUrlFromRoute($value); - return [ - 'value' => [ - '#type' => 'path', - '#default_value' => $value, - '#description' => $this->t("Enter an internal path"), - ], + $form['value'] = [ + '#type' => 'path', + '#default_value' => $value, + '#description' => $this->t("Enter an internal path"), ]; + return $form; } } diff --git a/src/Plugin/UiPatterns/Source/SelectWidget.php b/src/Plugin/UiPatterns/Source/SelectWidget.php index e80c6f145..5ab0bf362 100644 --- a/src/Plugin/UiPatterns/Source/SelectWidget.php +++ b/src/Plugin/UiPatterns/Source/SelectWidget.php @@ -25,17 +25,18 @@ class SelectWidget extends SourcePluginPropValue { * {@inheritdoc} */ public function settingsForm(array $form, FormStateInterface $form_state): array { - $build = []; - $build['value'] = [ + $form = parent::settingsForm($form, $form_state); + $form['value'] = [ '#type' => 'select', '#default_value' => $this->getSetting('value'), "#options" => $this->getOptions(), "#empty_option" => $this->t("- Select -"), ]; + $this->addRequired($form['value']); // With Firefox, autocomplete may override #default_value. // https://drupal.stackexchange.com/questions/257732/default-value-not-working-in-select-field - $build['value']['#attributes']['autocomplete'] = 'off'; - return $build; + $form['value']['#attributes']['autocomplete'] = 'off'; + return $form; } /** diff --git a/src/Plugin/UiPatterns/Source/TextfieldWidget.php b/src/Plugin/UiPatterns/Source/TextfieldWidget.php index a20a9f36a..427e4810d 100644 --- a/src/Plugin/UiPatterns/Source/TextfieldWidget.php +++ b/src/Plugin/UiPatterns/Source/TextfieldWidget.php @@ -25,28 +25,28 @@ class TextfieldWidget extends SourcePluginPropValue { * {@inheritdoc} */ public function settingsForm(array $form, FormStateInterface $form_state): array { - $build = []; - $build['value'] = [ + $form = parent::settingsForm($form, $form_state); + $form['value'] = [ '#type' => 'textfield', '#default_value' => $this->getSetting('value'), ]; + $this->addRequired($form['value']); $description = []; if (isset($this->propDefinition["pattern"])) { - $build['value']['#pattern'] = $this->propDefinition["pattern"]; + $form['value']['#pattern'] = $this->propDefinition["pattern"]; $description[] = $this->t("Constraint: @pattern", ["@pattern" => $this->propDefinition["pattern"]]); } if (isset($this->propDefinition["maxLength"])) { - $build['value']['#maxlength'] = $this->propDefinition["maxLength"]; - $build['value']['#size'] = $this->propDefinition["maxLength"]; + $form['value']['#maxlength'] = $this->propDefinition["maxLength"]; + $form['value']['#size'] = $this->propDefinition["maxLength"]; $description[] = $this->t("Max length: @length", ["@length" => $this->propDefinition["maxLength"]]); } if (!isset($this->propDefinition["pattern"]) && isset($this->propDefinition["minLength"])) { - // @todo Cover also the use case pattern + minLength. - $build['value']['#pattern'] = "^.{" . $this->propDefinition["minLength"] . ",}$"; + $form['value']['#pattern'] = "^.{" . $this->propDefinition["minLength"] . ",}$"; $description[] = $this->t("Min length: @length", ["@length" => $this->propDefinition["minLength"]]); } - $build['value']["#description"] = implode("; ", $description); - return $build; + $form['value']["#description"] = implode("; ", $description); + return $form; } } diff --git a/src/Plugin/UiPatterns/Source/TokenSource.php b/src/Plugin/UiPatterns/Source/TokenSource.php index 25edb3ad0..46b6f1800 100644 --- a/src/Plugin/UiPatterns/Source/TokenSource.php +++ b/src/Plugin/UiPatterns/Source/TokenSource.php @@ -68,15 +68,15 @@ class TokenSource extends SourcePluginBase { * {@inheritdoc} */ public function settingsForm(array $form, FormStateInterface $form_state): array { - return [ - 'value' => [ - '#type' => 'textfield', - '#default_value' => $this->getSetting('value'), - // Tokens always start with a [ and end with a ]. - '#pattern' => '^\[.+\]$', - ], - 'context_mapping' => $this->addContextAssignmentElement($this, $this->gatheredContexts), + $form = parent::settingsForm($form, $form_state); + $form['value'] = [ + '#type' => 'textfield', + '#default_value' => $this->getSetting('value'), + // Tokens always start with a [ and end with a ]. + '#pattern' => '^\[.+\]$', ]; + $this->addRequired($form['value']); + return $form; } } diff --git a/src/Plugin/UiPatterns/Source/UrlWidget.php b/src/Plugin/UiPatterns/Source/UrlWidget.php index 8fdd58e30..8bfefd637 100644 --- a/src/Plugin/UiPatterns/Source/UrlWidget.php +++ b/src/Plugin/UiPatterns/Source/UrlWidget.php @@ -25,13 +25,14 @@ class UrlWidget extends SourcePluginPropValue { * {@inheritdoc} */ public function settingsForm(array $form, FormStateInterface $form_state): array { - return [ - 'value' => [ - '#type' => 'url', - '#default_value' => $this->getSetting('value'), - '#description' => $this->t("Enter an external URL"), - ], + $form = parent::settingsForm($form, $form_state); + $form['value'] = [ + '#type' => 'url', + '#default_value' => $this->getSetting('value'), + '#description' => $this->t("Enter an external URL"), ]; + $this->addRequired($form['value']); + return $form; } } diff --git a/src/Plugin/UiPatterns/Source/WysiwygWidget.php b/src/Plugin/UiPatterns/Source/WysiwygWidget.php index 8bf60a769..8e7978145 100644 --- a/src/Plugin/UiPatterns/Source/WysiwygWidget.php +++ b/src/Plugin/UiPatterns/Source/WysiwygWidget.php @@ -87,6 +87,7 @@ class WysiwygWidget extends SourcePluginBase implements TrustedCallbackInterface * {@inheritdoc} */ public function settingsForm(array $form, FormStateInterface $form_state): array { + $form = parent::settingsForm($form, $form_state); $value = $this->getSetting('value'); $element = [ '#type' => 'text_format', @@ -101,7 +102,8 @@ class WysiwygWidget extends SourcePluginBase implements TrustedCallbackInterface else { $element['#format'] = filter_fallback_format(); } - return ['value' => $element]; + $form['value'] = $element; + return $form; } /** diff --git a/src/PropTypePluginBase.php b/src/PropTypePluginBase.php index 80279f4b1..aa4ffe514 100644 --- a/src/PropTypePluginBase.php +++ b/src/PropTypePluginBase.php @@ -56,6 +56,9 @@ abstract class PropTypePluginBase extends PluginBase implements PropTypeInterfac if (isset($definition['default']) && is_array($definition['default'])) { $summary[] = $this->t("Default: @default", ["@default" => implode(", ", $definition['default'])]); } + if (isset($definition['ui_patterns']['required']) && $definition['ui_patterns']['required']) { + $summary[] = $this->t("Required"); + } return $summary; } diff --git a/src/SourcePluginBase.php b/src/SourcePluginBase.php index cde9fb796..19c74ca95 100644 --- a/src/SourcePluginBase.php +++ b/src/SourcePluginBase.php @@ -142,7 +142,7 @@ abstract class SourcePluginBase extends PluginBase implements * {@inheritdoc} */ public function settingsForm(array $form, FormStateInterface $form_state): array { - return []; + return $form; } /** @@ -401,4 +401,21 @@ abstract class SourcePluginBase extends PluginBase implements } } + /** + * Add required property. + * + * @param array $form_element + * The form element. + * + * @see \Drupal\ui_patterns\Element\ComponentPropForm::addRequired() + */ + protected function addRequired(array &$form_element): void { + if (isset($this->propDefinition["ui_patterns"]["required"])) { + $form_element['#required'] = TRUE; + // ComponentPropForm is carrying the visual clue + // We set here the custom error message. + $form_element['#required_error'] = $this->t('@name field is required.', ['@name' => (!empty($form_element['#title'])) ? $form_element['#title'] : $this->propDefinition["title"]]); + } + } + } diff --git a/tests/modules/ui_patterns_test/src/Plugin/UiPatterns/Source/Foo.php b/tests/modules/ui_patterns_test/src/Plugin/UiPatterns/Source/Foo.php index 8b42eb6f1..7bee5c157 100644 --- a/tests/modules/ui_patterns_test/src/Plugin/UiPatterns/Source/Foo.php +++ b/tests/modules/ui_patterns_test/src/Plugin/UiPatterns/Source/Foo.php @@ -24,15 +24,14 @@ final class Foo extends SourcePluginBase { * {@inheritdoc} */ public function settingsForm(array $form, FormStateInterface $form_state): array { - return [ - "value" => [ - '#type' => 'textfield', - '#attributes' => [ - 'placeholder' => $this->t('Test: FOO'), - ], - '#default_value' => $this->getSetting('value'), + $form["value"] = [ + '#type' => 'textfield', + '#attributes' => [ + 'placeholder' => $this->t('Test: FOO'), ], + '#default_value' => $this->getSetting('value'), ]; + return $form; } /** -- GitLab