From 8fe23f0b5fa01a8bfb99302300baac2f22a845b7 Mon Sep 17 00:00:00 2001
From: "Christian.wiedemann"
 <7688-Christian.wiedemann@users.noreply.drupalcode.org>
Date: Mon, 26 Feb 2024 18:37:00 +0000
Subject: [PATCH] Issue #3414291 by pdureau, Christian.wiedemann: Source
 plugins, methods naming and interfaces use

---
 .../src/Plugin/Block/ComponentBlock.php       |   8 +-
 .../src/Plugin/Layout/ComponentLayout.php     |  14 +-
 .../ui_patterns_layouts.module                |   3 +-
 .../ui_patterns_legacy.module                 |   8 +-
 src/ComponentPluginManager.php                |   2 +-
 src/Element/ComponentElementAlter.php         |   3 +
 src/Element/ComponentElementBuilder.php       |  33 ++---
 src/Element/ComponentForm.php                 | 123 ++++++++----------
 src/Element/ComponentFormBase.php             |   5 +-
 src/Element/ComponentPropsForm.php            |   4 +-
 src/Element/ComponentSlotsForm.php            |   9 +-
 src/Form/ComponentFormBuilderTrait.php        |  49 ++++++-
 .../ComponentSettingsFormBuilderTrait.php     |  12 +-
 .../UiPatterns/Source/AttributesWidget.php    |   2 +-
 .../UiPatterns/Source/ChecboxesWidget.php     |  10 +-
 .../UiPatterns/Source/CheckboxWidget.php      |   8 +-
 src/Plugin/UiPatterns/Source/ColorWidget.php  |   8 +-
 .../UiPatterns/Source/ListTextareaWidget.php  |   2 +-
 src/Plugin/UiPatterns/Source/MenuSource.php   |  60 ++++++---
 src/Plugin/UiPatterns/Source/NumberWidget.php |   8 +-
 src/Plugin/UiPatterns/Source/PathSource.php   |  10 +-
 src/Plugin/UiPatterns/Source/RadiosWidget.php |  10 +-
 src/Plugin/UiPatterns/Source/SelectWidget.php |   4 +-
 .../UiPatterns/Source/TextfieldWidget.php     |  12 +-
 src/Plugin/UiPatterns/Source/TokenSource.php  |  10 +-
 src/Plugin/UiPatterns/Source/UrlWidget.php    |  10 +-
 .../UiPatterns/Source/WysiwygWidget.php       |   2 +-
 src/PluginSettingsInterface.php               |  77 +++++++++++
 src/SourceInterface.php                       |  66 +---------
 src/SourcePluginManager.php                   |   2 +-
 tests/src/Kernel/SourcePluginManagerTest.php  |  10 +-
 ui_patterns.services.yml                      |   4 +-
 32 files changed, 331 insertions(+), 257 deletions(-)
 create mode 100644 src/PluginSettingsInterface.php

diff --git a/modules/ui_patterns_blocks/src/Plugin/Block/ComponentBlock.php b/modules/ui_patterns_blocks/src/Plugin/Block/ComponentBlock.php
index 6d831958b..022a8307c 100644
--- a/modules/ui_patterns_blocks/src/Plugin/Block/ComponentBlock.php
+++ b/modules/ui_patterns_blocks/src/Plugin/Block/ComponentBlock.php
@@ -32,8 +32,7 @@ class ComponentBlock extends BlockBase {
    * {@inheritdoc}
    */
   public function build() {
-    $configuration = $this->getConfiguration();
-    $build = $this->buildComponentRenderable($configuration);
+    $build = $this->buildComponentRenderable();
     return $build;
   }
 
@@ -41,15 +40,14 @@ class ComponentBlock extends BlockBase {
    * {@inheritdoc}
    */
   public function blockForm($form, FormStateInterface $form_state) {
-    $configuration = $this->getConfiguration();
-    return $this->buildComponentsForm($form_state, $configuration['ui_patterns'], NULL, TRUE, TRUE);
+    return $this->buildComponentsForm($form_state, NULL, TRUE, TRUE);
   }
 
   /**
    * {@inheritdoc}
    */
   public function blockSubmit($form, FormStateInterface $form_state) {
-    $this->configuration['ui_patterns'] = $form_state->getValue('ui_patterns');
+    $this->submitComponentsForm($form_state);
   }
 
 }
diff --git a/modules/ui_patterns_layouts/src/Plugin/Layout/ComponentLayout.php b/modules/ui_patterns_layouts/src/Plugin/Layout/ComponentLayout.php
index 65e66e276..ef94b949d 100644
--- a/modules/ui_patterns_layouts/src/Plugin/Layout/ComponentLayout.php
+++ b/modules/ui_patterns_layouts/src/Plugin/Layout/ComponentLayout.php
@@ -42,14 +42,9 @@ class ComponentLayout extends LayoutDefault implements PluginFormInterface {
    * {@inheritdoc}
    */
   public function build(array $regions) {
-    $build = parent::build($regions);
-    $configuration = $this->getConfiguration();
-    $data = $this->buildComponentRenderable($configuration, $this->getPluginId());
+    $build = $this->buildComponentRenderable($this->getPluginDefinition()->id());
     $build['#layout'] = $this;
-    $build['#slots'] = $regions;
-    $build['#props'] = $data['#props'] ?? [];
-    $build['#type'] = $data['#type'];
-    $build['#component'] = $data['#component'];
+    $regions = parent::build($regions);
     foreach ($this->getPluginDefinition()->getRegionNames() as $region_name) {
       if (array_key_exists($region_name, $regions)) {
         $build['#slots'][$region_name] = $regions[$region_name];
@@ -70,9 +65,8 @@ class ComponentLayout extends LayoutDefault implements PluginFormInterface {
    */
   public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
     $form = parent::buildConfigurationForm($form, $form_state);
-    $configuration = $this->getConfiguration();
     $component_id = $this->getPluginDefinition()->id();
-    return $form + $this->buildComponentsForm($form_state, $configuration['ui_patterns'], $component_id, FALSE, TRUE);
+    return $form + $this->buildComponentsForm($form_state, $component_id, FALSE, TRUE);
   }
 
   /**
@@ -85,7 +79,7 @@ class ComponentLayout extends LayoutDefault implements PluginFormInterface {
    * {@inheritdoc}
    */
   public function submitConfigurationForm(array &$form, FormStateInterface $form_state):void {
-    $this->configuration['ui_patterns'] = $form_state->getValue('ui_patterns');
+    $this->submitComponentsForm($form_state);
     parent::submitConfigurationForm($form, $form_state);
   }
 
diff --git a/modules/ui_patterns_layouts/ui_patterns_layouts.module b/modules/ui_patterns_layouts/ui_patterns_layouts.module
index 0095c3e75..d5681b0d1 100644
--- a/modules/ui_patterns_layouts/ui_patterns_layouts.module
+++ b/modules/ui_patterns_layouts/ui_patterns_layouts.module
@@ -17,10 +17,9 @@ use Drupal\Core\Layout\LayoutDefinition;
  * Implements hook_layout_alter().
  */
 function ui_patterns_layouts_layout_alter(&$definitions) {
-  /** @var \Drupal\sdc\ComponentPluginManager $plugin_manager */
   $plugin_manager = \Drupal::service('plugin.manager.sdc');
   /** @var \Drupal\sdc\Component\ComponentMetadata[] $components */
-  $components = $plugin_manager->getDefinitions();
+  $components = $plugin_manager->getSortedDefinitions();
   foreach ($components as $component) {
     $definition = [
       'label' => $component['name'] ?? $component['id'],
diff --git a/modules/ui_patterns_legacy/ui_patterns_legacy.module b/modules/ui_patterns_legacy/ui_patterns_legacy.module
index bd7fac732..a1557eba4 100644
--- a/modules/ui_patterns_legacy/ui_patterns_legacy.module
+++ b/modules/ui_patterns_legacy/ui_patterns_legacy.module
@@ -14,6 +14,10 @@ function ui_patterns_legacy_element_info_alter(array &$types) {
     $types = _ui_patterns_legacy_clone_component_element($types, "pattern");
     $types = _ui_patterns_legacy_clone_component_element($types, "pattern_preview");
   }
+  $moduleHandler = \Drupal::service('module_handler');
+  if ($moduleHandler->moduleExists('ui_patterns_library')) {
+    array_unshift($types["pattern_preview"]['#pre_render'], 'ui_patterns_library.component_element_alter:alter');
+  }
 }
 
 /**
@@ -22,10 +26,6 @@ function ui_patterns_legacy_element_info_alter(array &$types) {
 function _ui_patterns_legacy_clone_component_element(array $types, string $element_id): array {
   $types[$element_id] = $types['component'];
   array_unshift($types[$element_id]['#pre_render'], 'ui_patterns.component_element_alter:alter');
-  $moduleHandler = \Drupal::service('module_handler');
-  if ($moduleHandler->moduleExists('ui_patterns_library')) {
-    array_unshift($types[$element_id]['#pre_render'], 'ui_patterns_library.component_element_alter:alter');
-  }
   array_unshift($types[$element_id]['#pre_render'], 'ui_patterns_legacy.component_element_alter:convert');
   return $types;
 }
diff --git a/src/ComponentPluginManager.php b/src/ComponentPluginManager.php
index b7c9da955..88a621c76 100644
--- a/src/ComponentPluginManager.php
+++ b/src/ComponentPluginManager.php
@@ -170,7 +170,7 @@ class ComponentPluginManager extends SdcPluginManager implements CategorizingPlu
     $label_key = 'name';
     uasort($definitions, function ($a, $b) use ($label_key) {
       if ((string) $a['group'] != (string) $b['group']) {
-        return strnatcasecmp($a['group'], $b['group']);
+        return strnatcasecmp($a['group'] ?? '', $b['group'] ?? '');
       }
       return strnatcasecmp($a[$label_key], $b[$label_key]);
     });
diff --git a/src/Element/ComponentElementAlter.php b/src/Element/ComponentElementAlter.php
index 9ba701c9c..28a516ca0 100644
--- a/src/Element/ComponentElementAlter.php
+++ b/src/Element/ComponentElementAlter.php
@@ -45,6 +45,9 @@ class ComponentElementAlter implements TrustedCallbackInterface {
       elseif (is_string($slot)) {
         $element['#slots'][$slot_id] = ['#markup' => $slot];
       }
+      elseif (is_string($slot)) {
+        $element['#slots'][$slot_id] = ['#markup' => $slot];
+      }
 
       // Because SDC validator is sometimes confused by a null slot.
       if (is_null($slot)) {
diff --git a/src/Element/ComponentElementBuilder.php b/src/Element/ComponentElementBuilder.php
index 5c51b630f..b19c4b106 100644
--- a/src/Element/ComponentElementBuilder.php
+++ b/src/Element/ComponentElementBuilder.php
@@ -39,15 +39,16 @@ class ComponentElementBuilder implements TrustedCallbackInterface {
    * Build component data provided to the SDC element.
    */
   public function build(array $element): array {
-    if (!isset($element['#configuration'])) {
+    if (!isset($element['#ui_patterns'])) {
       return $element;
     }
-    $configuration = $element['#configuration'];
+    $configuration = $element['#ui_patterns'];
     $contexts = $element['#source_contexts'] ?? [];
     $component = $this->componentPluginManager->find($element['#component']);
     $element = $this->buildProps($element, $component, $configuration, $contexts);
     $element = $this->buildSlots($element, $component, $configuration, $contexts);
-    return array_filter($element);
+    $element['#propsAlter'] = [];
+    return $element;
   }
 
   /**
@@ -69,6 +70,10 @@ class ComponentElementBuilder implements TrustedCallbackInterface {
    * Add a single prop to the renderable.
    */
   protected function buildProp(array $build, string $prop_id, array $definition, array $configuration, array $contexts): array {
+    if (isset($build["#props"][$prop_id])) {
+      // Keep existing props. No known use case yet.
+      return $build;
+    }
     $prop_type = $definition['ui_patterns']['type_definition'];
     $source_id = array_key_exists("source_id", $configuration) ? $configuration["source_id"] : $this->sourcesManager->getPropTypeDefault($prop_type->getPluginId(), $contexts);
     if (!$source_id) {
@@ -76,11 +81,7 @@ class ComponentElementBuilder implements TrustedCallbackInterface {
     }
     $source = $this->sourcesManager->createInstance(
       $source_id,
-      [
-        'prop_id' => $prop_id,
-        'prop_definition' => $definition,
-        'settings' => $configuration,
-      ]
+      SourcePluginManager::buildPluginConfiguration($prop_id, $definition, $configuration)
     );
     if (!$source) {
       return $build;
@@ -122,6 +123,10 @@ class ComponentElementBuilder implements TrustedCallbackInterface {
    * Add a single slot to the renderable.
    */
   protected function buildSlot(array $build, string $slot_id, array $definition, array $configuration, array $contexts): array {
+    if (isset($build["#slots"][$slot_id])) {
+      // Keep existing slots. Used by ComponentLayout for example.
+      return $build;
+    }
     if (!isset($configuration["sources"])) {
       return $build;
     }
@@ -130,8 +135,8 @@ class ComponentElementBuilder implements TrustedCallbackInterface {
     foreach ($configuration["sources"] as $source_configuration) {
       $build = $this->addSlotSource($build, $slot_id, $definition, $source_configuration, $contexts);
     }
-    if (count($build["#props"][$slot_id]) === 1) {
-      $build["#props"][$slot_id] = $build["#props"][$slot_id][0];
+    if (count($build["#slots"][$slot_id]) === 1) {
+      $build["#slots"][$slot_id] = $build["#slots"][$slot_id][0];
     }
     return $build;
   }
@@ -146,14 +151,10 @@ class ComponentElementBuilder implements TrustedCallbackInterface {
     }
     $source = $this->sourcesManager->createInstance(
         $source_id,
-        [
-          'prop_id' => $slot_id,
-          'prop_definition' => $definition,
-          'settings' => $configuration,
-        ]
+        SourcePluginManager::buildPluginConfiguration($slot_id, $definition, $configuration)
       );
     $build = $source->alterComponent($build);
-    $build["#props"][$slot_id][] = $source->getData();
+    $build["#slots"][$slot_id][] = $source->getData();
     return $build;
   }
 
diff --git a/src/Element/ComponentForm.php b/src/Element/ComponentForm.php
index 501ac7712..41efec21a 100644
--- a/src/Element/ComponentForm.php
+++ b/src/Element/ComponentForm.php
@@ -2,7 +2,6 @@
 
 namespace Drupal\ui_patterns\Element;
 
-use Drupal\Component\Utility\Html;
 use Drupal\Component\Utility\NestedArray;
 use Drupal\Core\Form\FormStateInterface;
 
@@ -75,6 +74,7 @@ class ComponentForm extends ComponentFormBase {
     if ($input) {
       $value = [
         'component_id' => $input['component_id'] ?? NULL,
+        'variant_id' => $input['component_form']['variant_id'] ?? NULL,
         'props' => $input['component_form']['props'] ?? [],
         'slots' => $input['component_form']['slots'] ?? [],
       ];
@@ -84,6 +84,7 @@ class ComponentForm extends ComponentFormBase {
     else {
       return [
         'component_id' => NULL,
+        'variant_id' => NULL,
         'props' => [],
         'slots' => [],
       ];
@@ -113,8 +114,7 @@ class ComponentForm extends ComponentFormBase {
     $element["component_form"] = self::buildComponentForm(
       $element,
       $wrapper_id,
-      $component_id,
-      $form_state
+      $component_id
     );
     $element['#tree'] = TRUE;
     return $element;
@@ -127,70 +127,27 @@ class ComponentForm extends ComponentFormBase {
    *   The component form.
    */
   private static function buildComponentForm(
-    &$element,
-    $wrapper_id,
-    $component_id,
-    FormStateInterface $form_state
+    array $element,
+    string $wrapper_id,
+    ?string $component_id
   ): array {
-
+    if (!$component_id) {
+      return $element;
+    }
     $form = [
       '#type' => 'container',
       '#prefix' => '<div id="' . $wrapper_id . '">',
       '#suffix' => '</div>',
     ];
-    if ($component_id !== NULL) {
-      $form['variant_id'] = self::buildComponentVariantSelectorForm(
-        $element,
-        $element['#default_value']['variant_id'] ?? NULL,
-      );
-      $form += self::buildComponentVariantForm(
-        $wrapper_id,
-        $element
-      );
-    }
-
+    $form['variant_id'] = self::buildComponentVariantSelectorForm(
+      $element,
+      $element['#default_value']['variant_id'] ?? NULL,
+    );
+    $form['slots'] = self::buildSlotsForm($element, $component_id);
+    $form['props'] = self::buildPropsForm($element, $component_id);
     return $form;
   }
 
-  /**
-   * Build the component variant form.
-   *
-   * @return array
-   *   The component variant form.
-   */
-  private static function buildComponentVariantForm(
-    $wrapper_id,
-    $element
-  ): array {
-    $component_id = $element['#default_value']['component_id'] ?? $element['#component_id'] ?? NULL;
-
-    return [
-      '#type' => 'container',
-      '#prefix' => '<div id="' . $wrapper_id . '">',
-      '#suffix' => '</div>',
-      'slots' => [
-        '#title' => 'Slots',
-        '#type' => 'component_slots_form',
-        '#component_id' => $component_id,
-        '#source_context' => $element['#source_context'],
-        '#ajax_url' => $element['#ajax_url'],
-        '#access' => $element['#render_slots'] ?? TRUE,
-        '#default_value' => [
-          'slots' => $element['#default_value']['slots'],
-        ],
-      ],
-      'props' => [
-        '#title' => 'Props',
-        '#type' => 'component_props_form',
-        '#component_id' => $component_id,
-        '#source_context' => $element['#source_context'],
-        '#ajax_url' => $element['#ajax_url'],
-        '#access' => $element['#render_props'] ?? TRUE,
-        '#default_value' => ['props' => $element['#default_value']['props']],
-      ],
-    ];
-  }
-
   /**
    * Build components selector widget.
    *
@@ -198,8 +155,8 @@ class ComponentForm extends ComponentFormBase {
    *   The component select.
    */
   private static function buildComponentSelectorForm(
-    $wrapper_id,
-    $selected_component_id
+    ?string $wrapper_id,
+    ?string $selected_component_id
   ): array {
     $definition_groups = \Drupal::service("plugin.manager.sdc")->getGroupedDefinitions();
     $options = [];
@@ -211,7 +168,6 @@ class ComponentForm extends ComponentFormBase {
       }
       $options[$definition_group_id] = $group_options;
     }
-
     return [
       "#type" => "select",
       "#title" => t("Component"),
@@ -244,17 +200,46 @@ class ComponentForm extends ComponentFormBase {
     foreach ($definition["variants"] as $variant_id => $variant) {
       $options[$variant_id] = $variant["title"];
     }
-    $id = strtr(Html::getId('variant_id'), '-', '_');
-
-    return self::expandAjax([
+    return [
       "#type" => "select",
-      '#name' => $id,
-      '#id' => $id,
       "#title" => t("Variant"),
       "#options" => $options,
-      '#ui_patterns' => ['id' => 'variant_id'],
       '#default_value' => $default_variant_id,
-    ]);
+    ];
+  }
+
+  /**
+   * Build slots form.
+   */
+  private static function buildSlotsForm(array $element, string $component_id): array {
+    return [
+      '#title' => 'Slots',
+      '#type' => 'component_slots_form',
+      '#component_id' => $component_id,
+      '#source_context' => $element['#source_context'],
+      '#ajax_url' => $element['#ajax_url'],
+      '#access' => $element['#render_slots'] ?? TRUE,
+      '#default_value' => [
+        'slots' => $element['#default_value']['slots'],
+      ],
+    ];
+  }
+
+  /**
+   * Build props form.
+   */
+  private static function buildPropsForm(array $element, string $component_id): array {
+    return [
+      '#title' => 'Props',
+      '#type' => 'component_props_form',
+      '#component_id' => $component_id,
+      '#source_context' => $element['#source_context'],
+      '#ajax_url' => $element['#ajax_url'],
+      '#access' => $element['#render_props'] ?? TRUE,
+      '#default_value' => [
+        'props' => $element['#default_value']['props'],
+      ],
+    ];
   }
 
   /**
@@ -273,7 +258,7 @@ class ComponentForm extends ComponentFormBase {
   /**
    * Form element validation handler.
    */
-  public static function validateFormElement(&$element, FormStateInterface $form_state, &$complete_form) {
+  public static function validateFormElement(array &$element, FormStateInterface $form_state) {
     $trigger_element = $form_state->getTriggeringElement();
     if (isset($trigger_element['#ui_patterns']) === FALSE) {
       $form_state->setValueForElement($element, $element['#value']);
diff --git a/src/Element/ComponentFormBase.php b/src/Element/ComponentFormBase.php
index 67bcdef73..68abb4708 100644
--- a/src/Element/ComponentFormBase.php
+++ b/src/Element/ComponentFormBase.php
@@ -3,7 +3,6 @@
 namespace Drupal\ui_patterns\Element;
 
 use Drupal\Component\Utility\Html;
-use Drupal\Core\Form\FormStateInterface;
 use Drupal\Core\Render\Element;
 use Drupal\Core\Render\Element\FormElement;
 use Drupal\Core\Url;
@@ -70,13 +69,11 @@ abstract class ComponentFormBase extends FormElement {
    *
    * @param array $button_base
    *   Button base render array.
-   * @param \Drupal\Core\Form\FormStateInterface $form_state
-   *   The form state.
    *
    * @return array
    *   Button render array.
    */
-  protected static function expandComponentButton(array $button_base, FormStateInterface $form_state) {
+  protected static function expandComponentButton(array $button_base) {
     // Do not expand elements that do not have submit handler.
     if (empty($button_base['#submit'])) {
       return $button_base;
diff --git a/src/Element/ComponentPropsForm.php b/src/Element/ComponentPropsForm.php
index 75e8bc04a..9f6b85f7c 100644
--- a/src/Element/ComponentPropsForm.php
+++ b/src/Element/ComponentPropsForm.php
@@ -100,7 +100,7 @@ class ComponentPropsForm extends ComponentFormBase {
       }
     }
     if ($selected_source) {
-      $element['value'] = $selected_source->settingsForm(
+      $element['plugin'] = $selected_source->settingsForm(
         $element,
         $form_state
       );
@@ -143,7 +143,7 @@ class ComponentPropsForm extends ComponentFormBase {
             'effect' => 'fade',
           ],
         ];
-        $actions[$source_id] = self::expandComponentButton($button, $form_state);
+        $actions[$source_id] = self::expandComponentButton($button);
       }
     }
     if (count($actions) === 0) {
diff --git a/src/Element/ComponentSlotsForm.php b/src/Element/ComponentSlotsForm.php
index e1de4cf41..822a0fc24 100644
--- a/src/Element/ComponentSlotsForm.php
+++ b/src/Element/ComponentSlotsForm.php
@@ -128,7 +128,7 @@ class ComponentSlotsForm extends ComponentFormBase {
         $configuration['source_id'],
         SourcePluginManager::buildPluginConfiguration($slot_id, $definition, $configuration)
       );
-    $element['value'] = $source->settingsForm([], $form_state);
+    $element['plugin'] = $source->settingsForm([], $form_state);
     $element['source_id'] = [
       '#type' => 'hidden',
       '#value' => $source->getPluginId(),
@@ -173,10 +173,7 @@ class ComponentSlotsForm extends ComponentFormBase {
       '#type' => 'ui_patterns_actions',
       '#ui_patterns_header' => TRUE,
       'dropdown_actions' => [
-        self::expandComponentButton(
-            $delete_action,
-            $form_state
-        ),
+        self::expandComponentButton($delete_action),
       ],
     ];
   }
@@ -206,7 +203,7 @@ class ComponentSlotsForm extends ComponentFormBase {
           'wrapper' => $wrapper_id,
           'effect' => 'fade',
         ],
-      ], $form_state);
+      ]);
     }
     return self::buildComponentDropbutton($action_buttons);
   }
diff --git a/src/Form/ComponentFormBuilderTrait.php b/src/Form/ComponentFormBuilderTrait.php
index 0f0ff52d8..7bc1f603f 100644
--- a/src/Form/ComponentFormBuilderTrait.php
+++ b/src/Form/ComponentFormBuilderTrait.php
@@ -13,6 +13,32 @@ use Drupal\Core\Url;
  */
 trait ComponentFormBuilderTrait {
 
+  /**
+   * Adapter function to get plugin configuration.
+   *
+   * Overwrite to return settings/options of the
+   * current plugin.
+   *
+   * @return array
+   *   The plugin settings/options.
+   */
+  protected function getComponentConfiguration(): array {
+    return $this->configuration['ui_patterns'] ?? [];
+  }
+
+  /**
+   * Adapter function to set plugin configuration.
+   *
+   * Overwrite to return settings/options of the
+   * current plugin.
+   *
+   * @param mixed $configuration
+   *   The configuration to store.
+   */
+  protected function setComponentConfiguration($configuration): void {
+    $this->configuration['ui_patterns'] = $configuration;
+  }
+
   /**
    * Get component form default.
    *
@@ -70,8 +96,6 @@ trait ComponentFormBuilderTrait {
    *
    * @param \Drupal\Core\Form\FormStateInterface $form_state
    *   The form state.
-   * @param array $configuration
-   *   The stored configuration.
    * @param string|null $initial_component_id
    *   The initial_component_id. If provided the component is changeable.
    * @param bool $render_slots
@@ -81,7 +105,6 @@ trait ComponentFormBuilderTrait {
    */
   protected function buildComponentsForm(
     FormStateInterface $form_state,
-    array $configuration,
     string $initial_component_id = NULL,
     bool $render_slots = TRUE,
     bool $render_props = TRUE
@@ -93,21 +116,33 @@ trait ComponentFormBuilderTrait {
         '#component_id' => $initial_component_id,
         '#ajax_url' => $this->getAjaxUrl($form_state),
         '#source_context' => $this->getComponentSourceContexts(),
-        '#default_value' => $configuration,
+        '#default_value' => $this->getComponentConfiguration(),
         '#render_slots' => $render_slots,
         '#render_props' => $render_props,
       ],
     ];
   }
 
+  /**
+   * Submit the component form.
+   *
+   * The form contains.
+   *
+   * @param \Drupal\Core\Form\FormStateInterface $form_state
+   *   The form state.
+   */
+  public function submitComponentsForm(FormStateInterface $form_state) {
+    $this->setComponentConfiguration($form_state->getValue('ui_patterns'));
+  }
+
   /**
    * Build component renderable (a SDC render element).
    */
-  public function buildCompnentRenderable(array $configuration, $component_id = NULL) {
+  public function buildComponentRenderable($component_id = NULL) {
     return [
-      '#component' => $component_id ?? $configuration['component_id'],
       '#type' => 'component',
-      '#ui_patterns' => $configuration,
+      '#component' => $component_id ?? $this->getComponentConfiguration()['component_id'],
+      '#ui_patterns' => $this->getComponentConfiguration(),
     ];
   }
 
diff --git a/src/Form/ComponentSettingsFormBuilderTrait.php b/src/Form/ComponentSettingsFormBuilderTrait.php
index 06582af22..26dfde3c9 100644
--- a/src/Form/ComponentSettingsFormBuilderTrait.php
+++ b/src/Form/ComponentSettingsFormBuilderTrait.php
@@ -14,7 +14,7 @@ trait ComponentSettingsFormBuilderTrait {
   use ComponentFormBuilderTrait;
 
   /**
-   * PropTypeAdapter function for plugin settings/options.
+   * Adapter function for plugin settings/options.
    *
    * Overwrite to return settings/options of the
    * current plugin.
@@ -24,6 +24,13 @@ trait ComponentSettingsFormBuilderTrait {
    */
   abstract protected function getComponentSettings(): array;
 
+  /**
+   * {@inheritdoc}
+   */
+  protected function getComponentConfiguration(): array {
+    return $this->getComponentSettings()['ui_patterns'] ?? [];
+  }
+
   /**
    * {@inheritdoc}
    */
@@ -31,8 +38,7 @@ trait ComponentSettingsFormBuilderTrait {
     array $form,
     FormStateInterface $form_state
   ): array {
-    $configuration = $this->getComponentSettings()['ui_patterns'] ?? [];
-    return $this->buildComponentsForm($form_state, $configuration);
+    return $this->buildComponentsForm($form_state);
   }
 
 }
diff --git a/src/Plugin/UiPatterns/Source/AttributesWidget.php b/src/Plugin/UiPatterns/Source/AttributesWidget.php
index e765344cd..02ef6336b 100644
--- a/src/Plugin/UiPatterns/Source/AttributesWidget.php
+++ b/src/Plugin/UiPatterns/Source/AttributesWidget.php
@@ -43,7 +43,7 @@ class AttributesWidget extends SourcePluginBase {
     // 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.
-    $form = [
+    $form['value'] = [
       '#type' => 'textfield',
       '#title' => $this->propDefinition['title'],
       '#default_value' => $this->getSetting('value'),
diff --git a/src/Plugin/UiPatterns/Source/ChecboxesWidget.php b/src/Plugin/UiPatterns/Source/ChecboxesWidget.php
index 480af424a..7cefbf05f 100644
--- a/src/Plugin/UiPatterns/Source/ChecboxesWidget.php
+++ b/src/Plugin/UiPatterns/Source/ChecboxesWidget.php
@@ -38,10 +38,12 @@ class ChecboxesWidget extends SourcePluginBase {
     }
     // @todo max & min
     return [
-      '#type' => 'checkboxes',
-      '#title' => $this->propDefinition['title'],
-      '#default_value' => $this->getSetting('value') ?? [],
-      "#options" => $options,
+      'value' => [
+        '#type' => 'checkboxes',
+        '#title' => $this->propDefinition['title'],
+        '#default_value' => $this->getSetting('value') ?? [],
+        "#options" => $options,
+      ],
     ];
   }
 
diff --git a/src/Plugin/UiPatterns/Source/CheckboxWidget.php b/src/Plugin/UiPatterns/Source/CheckboxWidget.php
index 75fa05769..15069ded8 100644
--- a/src/Plugin/UiPatterns/Source/CheckboxWidget.php
+++ b/src/Plugin/UiPatterns/Source/CheckboxWidget.php
@@ -33,9 +33,11 @@ class CheckboxWidget extends SourcePluginBase {
    */
   public function settingsForm(array $form, FormStateInterface $form_state): array {
     return [
-      '#type' => 'checkbox',
-      '#title' => $this->propDefinition['title'],
-      '#default_value' => $this->getSetting('value'),
+      'value' => [
+        '#type' => 'checkbox',
+        '#title' => $this->propDefinition['title'],
+        '#default_value' => $this->getSetting('value'),
+      ],
     ];
   }
 
diff --git a/src/Plugin/UiPatterns/Source/ColorWidget.php b/src/Plugin/UiPatterns/Source/ColorWidget.php
index f17c0a1ef..d1ac9582f 100644
--- a/src/Plugin/UiPatterns/Source/ColorWidget.php
+++ b/src/Plugin/UiPatterns/Source/ColorWidget.php
@@ -26,9 +26,11 @@ class ColorWidget extends SourcePluginBase {
    */
   public function settingsForm(array $form, FormStateInterface $form_state): array {
     return [
-      '#type' => 'color',
-      '#title' => $this->propDefinition['title'],
-      '#default_value' => $this->getSetting('value'),
+      'value' => [
+        '#type' => 'color',
+        '#title' => $this->propDefinition['title'],
+        '#default_value' => $this->getSetting('value'),
+      ],
     ];
   }
 
diff --git a/src/Plugin/UiPatterns/Source/ListTextareaWidget.php b/src/Plugin/UiPatterns/Source/ListTextareaWidget.php
index 3002cf089..9eb45bed6 100644
--- a/src/Plugin/UiPatterns/Source/ListTextareaWidget.php
+++ b/src/Plugin/UiPatterns/Source/ListTextareaWidget.php
@@ -37,7 +37,7 @@ class ListTextareaWidget extends SourcePluginBase {
     if (is_array($items)) {
       $items = implode("\r", $items);
     }
-    $form = [
+    $form['value'] = [
       '#type' => 'textarea',
       '#title' => $this->propDefinition['title'],
       '#default_value' => $items,
diff --git a/src/Plugin/UiPatterns/Source/MenuSource.php b/src/Plugin/UiPatterns/Source/MenuSource.php
index 055d4afe7..a7464663e 100644
--- a/src/Plugin/UiPatterns/Source/MenuSource.php
+++ b/src/Plugin/UiPatterns/Source/MenuSource.php
@@ -55,44 +55,67 @@ class MenuSource extends SourcePluginBase {
   /**
    * {@inheritdoc}
    */
-  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
-    $plugin = parent::create($container, $configuration, $plugin_id, $plugin_definition);
+  public static function create(
+    ContainerInterface $container,
+    array $configuration,
+    $plugin_id,
+    $plugin_definition
+  ) {
+    $plugin = parent::create(
+      $container,
+      $configuration,
+      $plugin_id,
+      $plugin_definition
+    );
     $plugin->menuLinkTree = $container->get('menu.link_tree');
     $plugin->entityTypeManager = $container->get('entity_type.manager');
     $plugin->moduleHandler = $container->get('module_handler');
     return $plugin;
   }
 
+  /**
+   * {@inheritdoc}
+   */
+  public function defaultSettings() {
+    return [
+      'menu' => NULL,
+      'level' => 1,
+      'depth' => 0,
+    ];
+  }
+
   /**
    * {@inheritdoc}
    */
   public function getData(): mixed {
-    $value = parent::getData();
-    if (!$value) {
+    $menu_id = $this->getSetting('menu');
+    if (!$menu_id) {
       return [];
     }
-    $this->menuId = $value["menu"];
-    return $this->getMenuItems($value);
+    $this->menuId = $menu_id;
+    return $this->getMenuItems();
   }
 
   /**
    * {@inheritdoc}
    */
-  public function settingsForm(array $form, FormStateInterface $form_state): array {
-    $value = $this->getSetting('value') ?? [];
+  public function settingsForm(
+    array $form,
+    FormStateInterface $form_state
+  ): array {
     $form = [];
     $form["menu"] = [
       '#type' => 'select',
       '#title' => $this->propDefinition['title'] . ": " . $this->t("Menu"),
       '#options' => $this->getMenuList(),
-      '#default_value' => \array_key_exists("menu", $value) ? $value["menu"] : "",
+      '#default_value' => $this->getSetting('menu'),
     ];
     $options = range(0, $this->menuLinkTree->maxDepth());
     unset($options[0]);
     $form['level'] = [
       '#type' => 'select',
       '#title' => $this->t('Initial visibility level'),
-      '#default_value' => \array_key_exists("level", $value) ? $value["level"] : 1,
+      '#default_value' => $this->getSetting('level'),
       '#options' => $options,
       '#required' => TRUE,
     ];
@@ -100,13 +123,14 @@ class MenuSource extends SourcePluginBase {
     $form['depth'] = [
       '#type' => 'select',
       '#title' => $this->t('Number of levels to display'),
-      '#default_value' => \array_key_exists("depth", $value) ? $value["depth"] : 0,
+      '#default_value' => $this->getSetting('depth'),
       '#options' => $options,
-      '#description' => $this->t('This maximum number includes the initial level and the final display is dependant of the pattern template.'),
+      '#description' => $this->t(
+        'This maximum number includes the initial level and the final display is dependant of the pattern template.'
+      ),
       '#required' => TRUE,
     ];
     return $form;
-
   }
 
   /**
@@ -133,10 +157,10 @@ class MenuSource extends SourcePluginBase {
    * @return array
    *   List of items.
    */
-  private function getMenuItems($value): array {
+  private function getMenuItems(): array {
     $menuLinkTree = $this->menuLinkTree;
-    $level = (int) \array_key_exists("level", $value) ? $value["level"] : 1;
-    $depth = (int) \array_key_exists("depth", $value) ? $value["depth"] : 0;
+    $level = (int) $this->getSetting('level');
+    $depth = (int) $this->getSetting('depth');
     $parameters = new MenuTreeParameters();
     $parameters->setMinDepth($level);
 
@@ -145,7 +169,9 @@ class MenuSource extends SourcePluginBase {
     // Hence this is a relative depth that we must convert to an actual
     // (absolute) depth, that may never exceed the maximum depth.
     if ($depth > 0) {
-      $parameters->setMaxDepth(min($level + $depth - 1, $menuLinkTree->maxDepth()));
+      $parameters->setMaxDepth(
+        min($level + $depth - 1, $menuLinkTree->maxDepth())
+      );
     }
 
     $tree = $menuLinkTree->load($this->menuId, $parameters);
diff --git a/src/Plugin/UiPatterns/Source/NumberWidget.php b/src/Plugin/UiPatterns/Source/NumberWidget.php
index d834839bf..75bceea96 100644
--- a/src/Plugin/UiPatterns/Source/NumberWidget.php
+++ b/src/Plugin/UiPatterns/Source/NumberWidget.php
@@ -37,20 +37,20 @@ class NumberWidget extends SourcePluginBase {
    * {@inheritdoc}
    */
   public function settingsForm(array $form, FormStateInterface $form_state): array {
-    $form = [
+    $form['value'] = [
       '#type' => 'number',
       '#title' => $this->propDefinition['title'],
       '#default_value' => $this->getSetting('value'),
       '#step' => 0.01,
     ];
     if ($this->propDefinition["type"] === "integer") {
-      $form['#step'] = 1;
+      $form['value']['#step'] = 1;
     }
     if (isset($this->propDefinition["minimum"])) {
-      $form['#min'] = $this->propDefinition["minimum"];
+      $form['value']['#min'] = $this->propDefinition["minimum"];
     }
     if (isset($this->propDefinition["maximum"])) {
-      $form['#max'] = $this->propDefinition["maximum"];
+      $form['value']['#max'] = $this->propDefinition["maximum"];
     }
     return $form;
   }
diff --git a/src/Plugin/UiPatterns/Source/PathSource.php b/src/Plugin/UiPatterns/Source/PathSource.php
index f0d80c495..b02c34e43 100644
--- a/src/Plugin/UiPatterns/Source/PathSource.php
+++ b/src/Plugin/UiPatterns/Source/PathSource.php
@@ -51,10 +51,12 @@ class PathSource extends SourcePluginBase {
     $value = $this->getSetting('value');
     $value = $this->getUrlFromRoute($value);
     return [
-      '#type' => 'path',
-      '#title' => $this->propDefinition['title'],
-      '#default_value' => $value,
-      '#description' => $this->t("Enter an internal path"),
+      'value' => [
+        '#type' => 'path',
+        '#title' => $this->propDefinition['title'],
+        '#default_value' => $value,
+        '#description' => $this->t("Enter an internal path"),
+      ],
     ];
   }
 
diff --git a/src/Plugin/UiPatterns/Source/RadiosWidget.php b/src/Plugin/UiPatterns/Source/RadiosWidget.php
index 6c3cba9b2..97250b097 100644
--- a/src/Plugin/UiPatterns/Source/RadiosWidget.php
+++ b/src/Plugin/UiPatterns/Source/RadiosWidget.php
@@ -37,10 +37,12 @@ class RadiosWidget extends SourcePluginBase {
       $options[$item] = $item;
     }
     return [
-      '#type' => 'radios',
-      '#title' => $this->propDefinition['title'],
-      '#default_value' => $this->getSetting('value'),
-      "#options" => $options,
+      'value' => [
+        '#type' => 'radios',
+        '#title' => $this->propDefinition['title'],
+        '#default_value' => $this->getSetting('value'),
+        "#options" => $options,
+      ],
     ];
   }
 
diff --git a/src/Plugin/UiPatterns/Source/SelectWidget.php b/src/Plugin/UiPatterns/Source/SelectWidget.php
index 106a37001..f165d740a 100644
--- a/src/Plugin/UiPatterns/Source/SelectWidget.php
+++ b/src/Plugin/UiPatterns/Source/SelectWidget.php
@@ -36,7 +36,7 @@ class SelectWidget extends SourcePluginBase {
     foreach ($this->propDefinition['enum'] as $item) {
       $options[$item] = $item;
     }
-    $form = [
+    $form['value'] = [
       '#type' => 'select',
       '#title' => $this->propDefinition['title'],
       '#default_value' => $this->getSetting('value'),
@@ -44,7 +44,7 @@ class SelectWidget extends SourcePluginBase {
     ];
     // With Firefox, autocomplete may override #default_value.
     // https://drupal.stackexchange.com/questions/257732/default-value-not-working-in-select-field
-    $form['#attributes']['autocomplete'] = 'off';
+    $form['value']['#attributes']['autocomplete'] = 'off';
     return $form;
   }
 
diff --git a/src/Plugin/UiPatterns/Source/TextfieldWidget.php b/src/Plugin/UiPatterns/Source/TextfieldWidget.php
index e318a276f..e4c691d5c 100644
--- a/src/Plugin/UiPatterns/Source/TextfieldWidget.php
+++ b/src/Plugin/UiPatterns/Source/TextfieldWidget.php
@@ -27,27 +27,27 @@ class TextfieldWidget extends SourcePluginBase {
    * {@inheritdoc}
    */
   public function settingsForm(array $form, FormStateInterface $form_state): array {
-    $form = [
+    $form['value'] = [
       '#type' => 'textfield',
       '#title' => $this->propDefinition['title'],
       '#default_value' => $this->getSetting('value'),
     ];
     $description = [];
     if (isset($this->propDefinition["pattern"])) {
-      $form['#pattern'] = $this->propDefinition["pattern"];
+      $form['value']['#pattern'] = $this->propDefinition["pattern"];
       $description[] = $this->t("Constraint: @pattern", ["@pattern" => $this->propDefinition["pattern"]]);
     }
     if (isset($this->propDefinition["maxLength"])) {
-      $form['#maxlength'] = $this->propDefinition["maxLength"];
-      $form['#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.
-      $form['#pattern'] = "^.{" . $this->propDefinition["minLength"] . ",}$";
+      $form['value']['#pattern'] = "^.{" . $this->propDefinition["minLength"] . ",}$";
       $description[] = $this->t("Min length: @length", ["@length" => $this->propDefinition["minLength"]]);
     }
-    $form["#description"] = implode("; ", $description);
+    $form['value']["#description"] = implode("; ", $description);
     return $form;
   }
 
diff --git a/src/Plugin/UiPatterns/Source/TokenSource.php b/src/Plugin/UiPatterns/Source/TokenSource.php
index 9f0e0456f..8febc2bf2 100644
--- a/src/Plugin/UiPatterns/Source/TokenSource.php
+++ b/src/Plugin/UiPatterns/Source/TokenSource.php
@@ -41,11 +41,13 @@ class TokenSource extends SourcePluginBase {
    */
   public function settingsForm(array $form, FormStateInterface $form_state): array {
     return [
-      '#type' => 'textfield',
-      '#title' => $this->t("@prop, with token", ["@prop" => $this->propDefinition['title']]),
-      '#default_value' => $this->getSetting('value'),
+      'value' => [
+        '#type' => 'textfield',
+        '#title' => $this->t("@prop, with token", ["@prop" => $this->propDefinition['title']]),
+        '#default_value' => $this->getSetting('value'),
       // Tokens always start with a [ and end with a ].
-      '#pattern' => '^\[.+\]$',
+        '#pattern' => '^\[.+\]$',
+      ],
     ];
   }
 
diff --git a/src/Plugin/UiPatterns/Source/UrlWidget.php b/src/Plugin/UiPatterns/Source/UrlWidget.php
index f4caa5537..d8175dd3c 100644
--- a/src/Plugin/UiPatterns/Source/UrlWidget.php
+++ b/src/Plugin/UiPatterns/Source/UrlWidget.php
@@ -26,10 +26,12 @@ class UrlWidget extends SourcePluginBase {
    */
   public function settingsForm(array $form, FormStateInterface $form_state): array {
     return [
-      '#type' => 'url',
-      '#title' => $this->propDefinition['title'],
-      '#default_value' => $this->getSetting('value'),
-      '#description' => $this->t("Enter an external URL"),
+      'value' => [
+        '#type' => 'url',
+        '#title' => $this->propDefinition['title'],
+        '#default_value' => $this->getSetting('value'),
+        '#description' => $this->t("Enter an external URL"),
+      ],
     ];
   }
 
diff --git a/src/Plugin/UiPatterns/Source/WysiwygWidget.php b/src/Plugin/UiPatterns/Source/WysiwygWidget.php
index 728c94a0e..8d0695285 100644
--- a/src/Plugin/UiPatterns/Source/WysiwygWidget.php
+++ b/src/Plugin/UiPatterns/Source/WysiwygWidget.php
@@ -38,7 +38,7 @@ class WysiwygWidget extends SourcePluginBase {
    */
   public function settingsForm(array $form, FormStateInterface $form_state): array {
     $value = $this->getSetting('value');
-    $form = [
+    $form['value'] = [
       '#type' => 'text_format',
       '#default_value' => $value["value"],
       '#format' => $value["format"],
diff --git a/src/PluginSettingsInterface.php b/src/PluginSettingsInterface.php
new file mode 100644
index 000000000..7e24a3781
--- /dev/null
+++ b/src/PluginSettingsInterface.php
@@ -0,0 +1,77 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Drupal\ui_patterns;
+
+use Drupal\Core\Form\FormStateInterface;
+
+/**
+ * Interface for source plugins.
+ */
+interface PluginSettingsInterface {
+
+  /**
+   * Defines the default settings for this plugin.
+   *
+   * @return array
+   *   A list of default settings, keyed by the setting name.
+   */
+  public function defaultSettings();
+
+  /**
+   * Returns the array of settings, including defaults for missing settings.
+   *
+   * @return array
+   *   The array of settings.
+   */
+  public function getSettings();
+
+  /**
+   * Returns the value of a setting, or its default value if absent.
+   *
+   * @param string $key
+   *   The setting name.
+   *
+   * @return mixed
+   *   The setting value.
+   */
+  public function getSetting($key);
+
+  /**
+   * Sets the settings for the plugin.
+   *
+   * @param array $settings
+   *   The array of settings, keyed by setting names. Missing settings will be
+   *   assigned their default values.
+   *
+   * @return $this
+   */
+  public function setSettings(array $settings);
+
+  /**
+   * Sets the value of a setting for the plugin.
+   *
+   * @param string $key
+   *   The setting name.
+   * @param mixed $value
+   *   The setting value.
+   *
+   * @return $this
+   */
+  public function setSetting($key, $value);
+
+  /**
+   * Returns a form to configure settings for the source plugins.
+   *
+   * @param array $form
+   *   The form where the settings form is being included in.
+   * @param \Drupal\Core\Form\FormStateInterface $form_state
+   *   The current state of the form.
+   *
+   * @return array
+   *   The form elements for the source settings.
+   */
+  public function settingsForm(array $form, FormStateInterface $form_state): array;
+
+}
diff --git a/src/SourceInterface.php b/src/SourceInterface.php
index 88346f944..639a43732 100644
--- a/src/SourceInterface.php
+++ b/src/SourceInterface.php
@@ -6,62 +6,11 @@ namespace Drupal\ui_patterns;
 
 use Drupal\Component\Plugin\ConfigurableInterface;
 use Drupal\Component\Plugin\PluginInspectionInterface;
-use Drupal\Core\Form\FormStateInterface;
 
 /**
  * Interface for source plugins.
  */
-interface SourceInterface extends ConfigurableInterface, PluginInspectionInterface {
-
-  /**
-   * Defines the default settings for this plugin.
-   *
-   * @return array
-   *   A list of default settings, keyed by the setting name.
-   */
-  public function defaultSettings();
-
-  /**
-   * Returns the array of settings, including defaults for missing settings.
-   *
-   * @return array
-   *   The array of settings.
-   */
-  public function getSettings();
-
-  /**
-   * Returns the value of a setting, or its default value if absent.
-   *
-   * @param string $key
-   *   The setting name.
-   *
-   * @return mixed
-   *   The setting value.
-   */
-  public function getSetting($key);
-
-  /**
-   * Sets the settings for the plugin.
-   *
-   * @param array $settings
-   *   The array of settings, keyed by setting names. Missing settings will be
-   *   assigned their default values.
-   *
-   * @return $this
-   */
-  public function setSettings(array $settings);
-
-  /**
-   * Sets the value of a setting for the plugin.
-   *
-   * @param string $key
-   *   The setting name.
-   * @param mixed $value
-   *   The setting value.
-   *
-   * @return $this
-   */
-  public function setSetting($key, $value);
+interface SourceInterface extends ConfigurableInterface, PluginInspectionInterface, PluginSettingsInterface {
 
   /**
    * Returns the translated plugin label.
@@ -73,19 +22,6 @@ interface SourceInterface extends ConfigurableInterface, PluginInspectionInterfa
    */
   public function getData(): mixed;
 
-  /**
-   * Returns a form to configure settings for the source plugins.
-   *
-   * @param array $form
-   *   The form where the settings form is being included in.
-   * @param \Drupal\Core\Form\FormStateInterface $form_state
-   *   The current state of the form.
-   *
-   * @return array
-   *   The form elements for the source settings.
-   */
-  public function settingsForm(array $form, FormStateInterface $form_state): array;
-
   /**
    * Returns the associated prop id.
    */
diff --git a/src/SourcePluginManager.php b/src/SourcePluginManager.php
index f7d7cf813..0dc77e1f5 100644
--- a/src/SourcePluginManager.php
+++ b/src/SourcePluginManager.php
@@ -75,7 +75,7 @@ class SourcePluginManager extends DefaultPluginManager implements ContextAwarePl
     return [
       'prop_id' => $prop_id,
       'prop_definition' => $prop_definition,
-      'settings' => $settings,
+      'settings' => $settings['plugin'] ?? [],
     ];
   }
 
diff --git a/tests/src/Kernel/SourcePluginManagerTest.php b/tests/src/Kernel/SourcePluginManagerTest.php
index 85d9f4d5f..ef7e4e8ee 100644
--- a/tests/src/Kernel/SourcePluginManagerTest.php
+++ b/tests/src/Kernel/SourcePluginManagerTest.php
@@ -33,9 +33,15 @@ final class SourcePluginManagerTest extends KernelTestBase {
    * Test callback.
    */
   public function testGetSourcePlugins(): void {
-    /** @var \Drupal\ui_patterns\SourcePluginManager $source_provider_plugin_manager */
     $source_plugin_manager = \Drupal::service('plugin.manager.ui_patterns_source');
-    $sources = $source_plugin_manager->getSourcePlugins('string', 'test', ['title' => 'test title']);
+    $source_ids = array_keys($source_plugin_manager->getDefinitionsForPropType('string'));
+    $configuration = [
+      'prop_id' => 'test',
+      'prop_definition' => ['title' => 'test title'],
+      'form_value' => [],
+    ];
+    $source_ids = array_combine($source_ids, $source_ids);
+    $sources = $source_plugin_manager->createInstances($source_ids, $configuration);
     $plugin_ids = [];
     /** @var \Drupal\ui_patterns\SourcePluginBase $source */
     foreach ($sources as $source) {
diff --git a/ui_patterns.services.yml b/ui_patterns.services.yml
index 1b03a88ee..07a6c9969 100644
--- a/ui_patterns.services.yml
+++ b/ui_patterns.services.yml
@@ -30,17 +30,17 @@ services:
     arguments:
       - "@plugin.manager.ui_patterns_prop_type"
 
-  # Builders
+  # Render elements management.
   ui_patterns.component_element_builder:
     class: Drupal\ui_patterns\Element\ComponentElementBuilder
     arguments:
       - "@plugin.manager.ui_patterns_source"
       - "@plugin.manager.ui_patterns_prop_type_adapter"
       - "@plugin.manager.sdc"
-
   ui_patterns.component_element_alter:
     class: Drupal\ui_patterns\Element\ComponentElementAlter
     arguments: []
+
   # JSON schema management.
   ui_patterns.schema_stream_wrapper:
     class: Drupal\ui_patterns\SchemaManager\StreamWrapper
-- 
GitLab