From b8f0fe19ed620d4ff65eeb0bb6c8e9ea0ddfb249 Mon Sep 17 00:00:00 2001
From: Mikael Meulle <21535-just_like_good_vibes@users.noreply.drupalcode.org>
Date: Mon, 14 Oct 2024 14:03:33 +0000
Subject: [PATCH] Issue #3477287 by just_like_good_vibes:  Rename MachineName
 to Identifier

---
 .../all_errors/all_errors.component.yml       |  8 +--
 .../components/all_errors/all_errors.twig     |  2 +-
 .../ui_patterns_legacy/src/PropConverter.php  |  4 +-
 ...amePropType.php => IdentifierPropType.php} | 12 +++--
 .../UiPatterns/PropType/StringPropType.php    |  7 ++-
 .../UiPatterns/Source/TextfieldWidget.php     | 49 ++++++++++++++++++-
 .../prop_types_tests.component.yml            |  2 +-
 .../prop_types_tests/prop_types_tests.twig    |  6 +--
 .../test-component.component.yml              |  4 +-
 .../test-component/test-component.twig        |  4 +-
 10 files changed, 72 insertions(+), 26 deletions(-)
 rename src/Plugin/UiPatterns/PropType/{MachineNamePropType.php => IdentifierPropType.php} (53%)

diff --git a/modules/ui_patterns_devel/tests/themes/ui_patterns_devel_theme_test/components/all_errors/all_errors.component.yml b/modules/ui_patterns_devel/tests/themes/ui_patterns_devel_theme_test/components/all_errors/all_errors.component.yml
index 13971d3a6..4dc908e8d 100644
--- a/modules/ui_patterns_devel/tests/themes/ui_patterns_devel_theme_test/components/all_errors/all_errors.component.yml
+++ b/modules/ui_patterns_devel/tests/themes/ui_patterns_devel_theme_test/components/all_errors/all_errors.component.yml
@@ -182,7 +182,7 @@ props:
           attributes: { $ref: ui-patterns://attributes }
           below: { $ref: ui-patterns://links }
           extra: { type: string }
-    test_prop_machine_name:
+    test_prop_identifier:
       title: Machine name
       type: string
       pattern: '^[A-Za-z]+\w*$'
@@ -236,9 +236,9 @@ props:
     # v2_test_prop_attributes:
     #   title: Test attributes
     #   $ref: 'ui-patterns://attributes'
-    # v2_test_prop_machine_name:
+    # v2_test_prop_identifier:
     # title: Test machine name
-    # $ref: 'ui-patterns://machine_name'
+    # $ref: 'ui-patterns://identifier'
 stories:
   preview:
     title: Preview
@@ -330,7 +330,7 @@ stories:
         - title: Bar
           url: '#bar'
           extra: Extra
-      test_prop_machine_name: foo_bar
+      test_prop_identifier: foo_bar
       test_prop_list:
         - Foo
         - Bar
diff --git a/modules/ui_patterns_devel/tests/themes/ui_patterns_devel_theme_test/components/all_errors/all_errors.twig b/modules/ui_patterns_devel/tests/themes/ui_patterns_devel_theme_test/components/all_errors/all_errors.twig
index cd4f4c062..1a9e70534 100644
--- a/modules/ui_patterns_devel/tests/themes/ui_patterns_devel_theme_test/components/all_errors/all_errors.twig
+++ b/modules/ui_patterns_devel/tests/themes/ui_patterns_devel_theme_test/components/all_errors/all_errors.twig
@@ -46,7 +46,7 @@ test_prop_links_2:
     - {{ sub_link.title }} -
   {% endfor %}
 {% endfor %}
-test_prop_machine_name: {{ test_prop_machine_name }}
+test_prop_identifier: {{ test_prop_identifier }}
 test_prop_list: {{ test_prop_list | join(', ') }}
 {# @todo test_prop_attributes_implicit: {{ test_prop_attributes_implicit }} #}
 test_prop_attributes_explicit: {{ test_prop_attributes_explicit }}
diff --git a/modules/ui_patterns_legacy/src/PropConverter.php b/modules/ui_patterns_legacy/src/PropConverter.php
index 96c8da01f..2691949ad 100644
--- a/modules/ui_patterns_legacy/src/PropConverter.php
+++ b/modules/ui_patterns_legacy/src/PropConverter.php
@@ -24,8 +24,8 @@ class PropConverter {
       'links' => [
         '$ref' => 'ui-patterns://links',
       ],
-      'machine_name' => [
-        '$ref' => 'ui-patterns://machine_name',
+      'identifier' => [
+        '$ref' => 'ui-patterns://identifier',
       ],
       'number' => $this->convertNumber($setting),
       'radios' => $this->convertEnum($setting),
diff --git a/src/Plugin/UiPatterns/PropType/MachineNamePropType.php b/src/Plugin/UiPatterns/PropType/IdentifierPropType.php
similarity index 53%
rename from src/Plugin/UiPatterns/PropType/MachineNamePropType.php
rename to src/Plugin/UiPatterns/PropType/IdentifierPropType.php
index a024e5511..1974e5a15 100644
--- a/src/Plugin/UiPatterns/PropType/MachineNamePropType.php
+++ b/src/Plugin/UiPatterns/PropType/IdentifierPropType.php
@@ -10,15 +10,17 @@ use Drupal\ui_patterns\PropTypePluginBase;
 
 /**
  * Provides a 'Machine name' PropType.
+ *
+ * @see https://developer.mozilla.org/en-US/docs/Web/CSS/ident
  */
 #[PropType(
-  id: 'machine_name',
-  label: new TranslatableMarkup('Machine name'),
-  description: new TranslatableMarkup('A string with restricted characters.'),
+  id: 'identifier',
+  label: new TranslatableMarkup('Identifier'),
+  description: new TranslatableMarkup('A string with restricted characters, suitable for an HTML ID.'),
   default_source: 'textfield',
-  schema: ['type' => 'string', 'pattern' => '^[A-Za-z]+\w*$'],
+  schema: ['type' => 'string', 'pattern' => '(?:--|-?[A-Za-z_\x{00A0}-\x{10FFFF}])[A-Za-z0-9-_\x{00A0}-\x{10FFFF}\.]*'],
   priority: 100
 )]
-class MachineNamePropType extends PropTypePluginBase {
+class IdentifierPropType extends PropTypePluginBase {
 
 }
diff --git a/src/Plugin/UiPatterns/PropType/StringPropType.php b/src/Plugin/UiPatterns/PropType/StringPropType.php
index a593bf08e..f68c0898e 100644
--- a/src/Plugin/UiPatterns/PropType/StringPropType.php
+++ b/src/Plugin/UiPatterns/PropType/StringPropType.php
@@ -16,10 +16,10 @@ use Drupal\ui_patterns\PropTypePluginBase;
   label: new TranslatableMarkup('String'),
   description: new TranslatableMarkup('Strings of text. May contain Unicode characters.'),
   default_source: 'textfield',
-  convert_from: ['number', 'url', 'machine_name'],
+  convert_from: ['number', 'url', 'identifier'],
   schema: ['type' => 'string'],
   priority: 1,
-  typed_data: ['datetime_iso8601', 'email', 'string']
+  typed_data: ['datetime_iso8601', 'email', 'string', 'machine_name', 'filter_format']
 )]
 class StringPropType extends PropTypePluginBase {
 
@@ -31,8 +31,7 @@ class StringPropType extends PropTypePluginBase {
       'boolean' => (string) $value,
       'number' => (string) $value,
       'url' => $value,
-      'machine_name' => $value,
-      'color' => $value,
+      'identifier' => $value,
       'string' => $value,
     };
   }
diff --git a/src/Plugin/UiPatterns/Source/TextfieldWidget.php b/src/Plugin/UiPatterns/Source/TextfieldWidget.php
index 427e4810d..b476b2869 100644
--- a/src/Plugin/UiPatterns/Source/TextfieldWidget.php
+++ b/src/Plugin/UiPatterns/Source/TextfieldWidget.php
@@ -5,6 +5,7 @@ declare(strict_types=1);
 namespace Drupal\ui_patterns\Plugin\UiPatterns\Source;
 
 use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Security\TrustedCallbackInterface;
 use Drupal\Core\StringTranslation\TranslatableMarkup;
 use Drupal\ui_patterns\Attribute\Source;
 use Drupal\ui_patterns\SourcePluginPropValue;
@@ -16,10 +17,47 @@ use Drupal\ui_patterns\SourcePluginPropValue;
   id: 'textfield',
   label: new TranslatableMarkup('Textfield'),
   description: new TranslatableMarkup('One-line text field.'),
-  prop_types: ['string', 'machine_name'],
+  prop_types: ['string', 'identifier'],
   tags: ['widget', 'widget:dismissible']
 )]
-class TextfieldWidget extends SourcePluginPropValue {
+class TextfieldWidget extends SourcePluginPropValue implements TrustedCallbackInterface {
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function trustedCallbacks() {
+    return ['validatePattern'];
+  }
+
+  /**
+   * Validate pattern.
+   *
+   * #element_validate callback for #pattern form element property.
+   *
+   * @param array $element
+   *   An associative array containing the properties and children of the
+   *    generic form element.
+   * @param \Drupal\Core\Form\FormStateInterface $form_state
+   *   The current state of the form.
+   *
+   * @see https://www.drupal.org/project/drupal/issues/2633550
+   * @see https://www.drupal.org/project/webform/issues/3002374
+   */
+  public static function validatePattern(array &$element, FormStateInterface $form_state) : void {
+    if ($element['#value'] !== '') {
+      // JavaScript-escaped Unicode characters to PCRE escape sequence format.
+      $pcre_pattern = preg_replace('/\\\\u([a-fA-F0-9]{4})/', '\\x{\\1}', $element['#pattern']);
+      $pattern = '{^(?:' . $pcre_pattern . ')$}u';
+      if (!preg_match($pattern, $element['#value'])) {
+        if (!empty($element['#pattern_error'])) {
+          $form_state->setError($element, $element['#pattern_error']);
+        }
+        else {
+          $form_state->setError($element, t('%name field is not in the right format.', ['%name' => $element['#title']]));
+        }
+      }
+    }
+  }
 
   /**
    * {@inheritdoc}
@@ -45,7 +83,14 @@ class TextfieldWidget extends SourcePluginPropValue {
       $form['value']['#pattern'] = "^.{" . $this->propDefinition["minLength"] . ",}$";
       $description[] = $this->t("Min length: @length", ["@length" => $this->propDefinition["minLength"]]);
     }
+    if (isset($form['value']['#pattern']) && !isset($form['value']['#title'])) {
+      $form['value']['#title'] = $this->propDefinition["title"] ?? $this->propId;
+    }
     $form['value']["#description"] = implode("; ", $description);
+    // @todo change when issue https://www.drupal.org/project/drupal/issues/2633550 is fixed.
+    if (isset($form['value']['#pattern'])) {
+      $form['value']['#element_validate'][] = [static::class, 'validatePattern'];
+    }
     return $form;
   }
 
diff --git a/tests/modules/ui_patterns_test/components/prop_types_tests/prop_types_tests.component.yml b/tests/modules/ui_patterns_test/components/prop_types_tests/prop_types_tests.component.yml
index 96552195c..5883dcde9 100644
--- a/tests/modules/ui_patterns_test/components/prop_types_tests/prop_types_tests.component.yml
+++ b/tests/modules/ui_patterns_test/components/prop_types_tests/prop_types_tests.component.yml
@@ -30,7 +30,7 @@ props:
       type: "string"
       maxLength: 10
       minLength: 3
-    machine_name:
+    identifier:
       title: "Machine name"
       type: string
       pattern: '^[A-Za-z]+\w*$'
diff --git a/tests/modules/ui_patterns_test/components/prop_types_tests/prop_types_tests.twig b/tests/modules/ui_patterns_test/components/prop_types_tests/prop_types_tests.twig
index 71857c7fc..53badc401 100644
--- a/tests/modules/ui_patterns_test/components/prop_types_tests/prop_types_tests.twig
+++ b/tests/modules/ui_patterns_test/components/prop_types_tests/prop_types_tests.twig
@@ -12,11 +12,11 @@
 {{ string_length }}
 
 {# ------------
-   machine_name
+   identifier
 ------------ #}
 
-<h3>machine_name</h3>
-{{ machine_name }}
+<h3>identifier</h3>
+{{ identifier }}
 
 {# ------------
    url
diff --git a/tests/modules/ui_patterns_test/components/test-component/test-component.component.yml b/tests/modules/ui_patterns_test/components/test-component/test-component.component.yml
index 6403451d9..83fb58fa8 100644
--- a/tests/modules/ui_patterns_test/components/test-component/test-component.component.yml
+++ b/tests/modules/ui_patterns_test/components/test-component/test-component.component.yml
@@ -14,9 +14,9 @@ props:
     url:
       title: "URL"
       $ref: "ui-patterns://url"
-    machine_name:
+    identifier:
       title: "Machine Name"
-      $ref: "ui-patterns://machine_name"
+      $ref: "ui-patterns://identifier"
     boolean:
       title: "Boolean"
       $ref: "ui-patterns://boolean"
diff --git a/tests/modules/ui_patterns_test/components/test-component/test-component.twig b/tests/modules/ui_patterns_test/components/test-component/test-component.twig
index 59c869543..63caaecf2 100644
--- a/tests/modules/ui_patterns_test/components/test-component/test-component.twig
+++ b/tests/modules/ui_patterns_test/components/test-component/test-component.twig
@@ -11,8 +11,8 @@
   <div class="ui-patterns-props-url">
     {{ url }}
   </div>
-  <div class="ui-patterns-props-machine_name">
-    {{ machine_name }}
+  <div class="ui-patterns-props-identifier">
+    {{ identifier }}
   </div>
   <div class="ui-patterns-props-boolean">
     {{ boolean }}
-- 
GitLab