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 13971d3a6cc53979f067c6b88c89d11d5da4057b..4dc908e8db6c08e7ef591bed59f66112c35a8ea9 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 cd4f4c062d7d6d4e2f6693d20f1ac548465115be..1a9e7053461e2a8e1245446d39a098f48bf9fbda 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 96c8da01fc7a0b845acfbc32a3a94fe8995e6217..2691949ad03e7c541ce270615e2fc0a3c21c8252 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 a024e55115da243b63bb13e2a67a3dd521527a84..1974e5a15eca6dfb14d6f05e11a2f3764ae0b01d 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 a593bf08ecb29f126c2af974af9a17b12a2002f7..f68c0898ef08b00848fb45e0c52f9c2ce79d2f1d 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 427e4810ddffd073de35273d1a8b929a2be58843..b476b28694f215cc4c5b4098215eb7e7a7c9500f 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 96552195cccd33fc9bdad88e8680040dabc60958..5883dcde9b541706f5319e6a58146516f3b343cb 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 71857c7fc5777b93594ef223d38d397ec98aa6b7..53badc4012ec83bfb70e67fbc60b61df4d35a95e 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 6403451d957426b44e6a0ce53275089d8c5cd5e9..83fb58fa8661e8d7b7e4616d64243863f1aa2e3c 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 59c8695435a0cb1ab58e8936fe2cd629973eff0d..63caaecf27f4fabfff53682d4991cd5bd8ad4025 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 }}