From da8a3360afc3d62a52bf99061d986cde264dab69 Mon Sep 17 00:00:00 2001 From: Lauri Eskola <lauri.eskola@acquia.com> Date: Fri, 15 Dec 2023 13:48:47 +0200 Subject: [PATCH] Issue #3408326 by benjifisher, omkar.podey, lauriii, srishtiiee: Make field selection a two-step form --- .../src/Functional/CommentFieldsTest.php | 5 +- .../EntityReferenceAdminTest.php | 4 +- .../field_ui/src/Form/FieldStorageAddForm.php | 353 ++++++++++-------- .../Functional/ManageFieldsFunctionalTest.php | 35 +- .../tests/src/Functional/ManageFieldsTest.php | 25 +- .../FunctionalJavascript/ManageFieldsTest.php | 38 +- .../tests/src/Traits/FieldUiJSTestTrait.php | 4 +- .../tests/src/Traits/FieldUiTestTrait.php | 31 +- core/modules/media/media.module | 2 +- .../MediaReferenceFieldHelpTest.php | 6 +- .../FieldUiIntegrationTest.php | 1 + .../Functional/NodeTypeTranslationTest.php | 2 + 12 files changed, 305 insertions(+), 201 deletions(-) diff --git a/core/modules/comment/tests/src/Functional/CommentFieldsTest.php b/core/modules/comment/tests/src/Functional/CommentFieldsTest.php index ad42c61a6553..0ff66475f961 100644 --- a/core/modules/comment/tests/src/Functional/CommentFieldsTest.php +++ b/core/modules/comment/tests/src/Functional/CommentFieldsTest.php @@ -154,10 +154,13 @@ public function testCommentFieldCreate() { // Create comment field in account settings. $edit = [ 'new_storage_type' => 'comment', + ]; + $this->drupalGet('admin/config/people/accounts/fields/add-field'); + $this->submitForm($edit, 'Continue'); + $edit = [ 'label' => 'User comment', 'field_name' => 'user_comment', ]; - $this->drupalGet('admin/config/people/accounts/fields/add-field'); $this->submitForm($edit, 'Continue'); // Try to save the comment field without selecting a comment type. diff --git a/core/modules/field/tests/src/FunctionalJavascript/EntityReference/EntityReferenceAdminTest.php b/core/modules/field/tests/src/FunctionalJavascript/EntityReference/EntityReferenceAdminTest.php index e7ed6abe1f9a..9249c4ebfd61 100644 --- a/core/modules/field/tests/src/FunctionalJavascript/EntityReference/EntityReferenceAdminTest.php +++ b/core/modules/field/tests/src/FunctionalJavascript/EntityReference/EntityReferenceAdminTest.php @@ -125,10 +125,12 @@ public function testFieldAdminHandler() { // Check if the commonly referenced entity types appear in the list. $page->find('css', "[name='new_storage_type'][value='reference']")->getParent()->click(); - $assert_session->waitForText('Choose an option below'); + $page->pressButton('Continue'); + $assert_session->pageTextContains('Choose an option below'); $this->assertSession()->elementExists('css', "[name='group_field_options_wrapper'][value='field_ui:entity_reference:node']"); $this->assertSession()->elementExists('css', "[name='group_field_options_wrapper'][value='field_ui:entity_reference:user']"); + $page->pressButton('Back'); $this->fieldUIAddNewFieldJS(NULL, 'test', 'Test', 'entity_reference', FALSE); // Node should be selected by default. diff --git a/core/modules/field_ui/src/Form/FieldStorageAddForm.php b/core/modules/field_ui/src/Form/FieldStorageAddForm.php index 6d5ff8bc3145..242ea7b9a8a8 100644 --- a/core/modules/field_ui/src/Form/FieldStorageAddForm.php +++ b/core/modules/field_ui/src/Form/FieldStorageAddForm.php @@ -131,57 +131,167 @@ public function buildForm(array $form, FormStateInterface $form_state, $entity_t $this->entityTypeId = $form_state->get('entity_type_id'); $this->bundle = $form_state->get('bundle'); + if (!$form_state->has('field_type_options') || !$form_state->has('unique_definitions')) { + $this->processFieldDefinitions($form_state); + } + + // Place the 'translatable' property as an explicit value so that contrib + // modules can form_alter() the value for newly created fields. By default + // we create field storage as translatable so it will be possible to enable + // translation at field level. + $form['translatable'] = [ + '#type' => 'value', + '#value' => TRUE, + ]; + + $form['actions'] = ['#type' => 'actions']; + $form['actions']['submit'] = [ + '#type' => 'submit', + '#value' => $this->t('Continue'), + '#button_type' => 'primary', + ]; + + $form['#attached']['library'] = [ + 'field_ui/drupal.field_ui', + 'field_ui/drupal.field_ui.manage_fields', + 'core/drupal.ajax', + ]; + // The group info is stored in new_storage_type. + if ($form_state->getValue('new_storage_type')) { + // A group is already selected. Show field types for that group. + $this->addFieldOptionsForGroup($form, $form_state); + } + else { + // Show options for groups and ungrouped field types. + $this->addGroupFieldOptions($form, $form_state); + } + + return $form; + } + + /** + * Adds field types for the selected group to the form. + * + * @param array $form + * An associative array containing the structure of the form. + * @param \Drupal\Core\Form\FormStateInterface $form_state + * The current state of the form. + */ + protected function addFieldOptionsForGroup(array &$form, FormStateInterface $form_state): void { // Field label and field_name. $form['new_storage_wrapper'] = [ '#type' => 'container', '#attributes' => [ 'class' => ['field-ui-new-storage-wrapper'], ], - '#states' => [ - '!visible' => [ - ':input[name="new_storage_type"]' => ['value' => ''], - ], - ], ]; $form['new_storage_wrapper']['label'] = [ '#type' => 'textfield', '#title' => $this->t('Label'), '#size' => 30, ]; + $field_prefix = $this->config('field_ui.settings')->get('field_prefix'); + $form['new_storage_wrapper']['field_name'] = [ + '#type' => 'machine_name', + '#field_prefix' => $field_prefix, + '#size' => 15, + '#description' => $this->t('A unique machine-readable name containing letters, numbers, and underscores.'), + // Calculate characters depending on the length of the field prefix + // setting. Maximum length is 32. + '#maxlength' => FieldStorageConfig::NAME_MAX_LENGTH - strlen($field_prefix), + '#machine_name' => [ + 'source' => ['new_storage_wrapper', 'label'], + 'exists' => [$this, 'fieldNameExists'], + ], + '#required' => FALSE, + ]; - $field_type_options = $unique_definitions = []; - $grouped_definitions = $this->fieldTypePluginManager->getGroupedDefinitions($this->fieldTypePluginManager->getEntityTypeUiDefinitions($this->entityTypeId), 'label', 'id'); - $category_definitions = $this->fieldTypeCategoryManager->getDefinitions(); - // Invoke a hook to get category properties. - foreach ($grouped_definitions as $category => $field_types) { - foreach ($field_types as $name => $field_type) { - $unique_definitions[$category][$name] = ['unique_identifier' => $name] + $field_type; - if ($this->fieldTypeCategoryManager->hasDefinition($category)) { - $category_plugin = $this->fieldTypeCategoryManager->createInstance($category, $unique_definitions[$category][$name], $category_definitions[$category]); - $field_type_options[$category_plugin->getPluginId()] = ['unique_identifier' => $name] + $field_type; - } - else { - $field_type_options[(string) $field_type['label']] = ['unique_identifier' => $name] + $field_type; - } - } + $form['actions']['submit']['#validate'][] = '::validateAddNew'; + + $form['actions']['back'] = [ + '#type' => 'submit', + '#value' => $this->t('Back'), + '#submit' => ['::startOver'], + ]; + + $field_type_options = $form_state->get('field_type_options'); + $new_storage_type = $form_state->getValue('new_storage_type'); + $form['new_storage_type'] = [ + '#type' => 'value', + '#value' => $new_storage_type, + ]; + if (!isset($new_storage_type) || !$field_type_options[$new_storage_type]['display_as_group']) { + return; } - $form['add-label'] = [ + // Create a wrapper for all the field options to be provided. + $form['group_field_options_wrapper'] = [ + '#prefix' => '<div id="group-field-options-wrapper" class="group-field-options-wrapper">', + '#suffix' => '</div>', + ]; + $form['group_field_options_wrapper']['label'] = [ '#type' => 'label', - '#title' => t('Choose a type of field'), + '#title' => $this->t('Choose an option below'), '#required' => TRUE, ]; - - $form['add'] = [ + $form['group_field_options_wrapper']['fields'] = [ '#type' => 'container', '#attributes' => [ - 'class' => 'add-field-container', + 'class' => ['group-field-options'], ], ]; + + $unique_definitions = $form_state->get('unique_definitions'); + $group_field_options = []; + foreach ($unique_definitions[$new_storage_type] as $option_key => $option) { + $radio_element = [ + '#type' => 'radio', + '#theme_wrappers' => ['form_element__new_storage_type'], + '#title' => $option['label'], + '#description' => [ + '#theme' => 'item_list', + '#items' => $unique_definitions[$new_storage_type][$option_key]['description'], + ], + '#id' => $option['unique_identifier'], + '#weight' => $option['weight'], + '#parents' => ['group_field_options_wrapper'], + '#attributes' => [ + 'class' => ['field-option-radio'], + 'data-once' => 'field-click-to-select', + ], + '#wrapper_attributes' => [ + 'class' => ['js-click-to-select', 'subfield-option'], + ], + '#variant' => 'field-suboption', + ]; + $radio_element['#return_value'] = $option['unique_identifier']; + if ((string) $option['unique_identifier'] === 'entity_reference') { + $radio_element['#title'] = 'Other'; + $radio_element['#weight'] = 10; + } + $group_field_options[$option['unique_identifier']] = $radio_element; + } + uasort($group_field_options, [SortArray::class, 'sortByWeightProperty']); + $form['group_field_options_wrapper']['fields'] += $group_field_options; + } + + /** + * Adds ungrouped field types and field type groups to the form. + * + * When a group is selected, the related fields are shown when the form is + * rebuilt. + * + * @param array $form + * An associative array containing the structure of the form. + * @param \Drupal\Core\Form\FormStateInterface $form_state + * The current state of the form. + */ + protected function addGroupFieldOptions(array &$form, FormStateInterface $form_state): void { + $field_type_options = $form_state->get('field_type_options'); $field_type_options_radios = []; foreach ($field_type_options as $id => $field_type) { /** @var \Drupal\Core\Field\FieldTypeCategoryInterface $category_info */ $category_info = $this->fieldTypeCategoryManager->createInstance($field_type['category'], $field_type); - $display_as_group = !($category_info instanceof FallbackFieldTypeCategory); + $display_as_group = $field_type['display_as_group']; $cleaned_class_name = Html::getClass($field_type['unique_identifier']); $field_type_options_radios[$id] = [ '#type' => 'container', @@ -204,10 +314,6 @@ public function buildForm(array $form, FormStateInterface $form_state, $entity_t ], ], ], - // Store some data we later need. - '#data' => [ - '#group_display' => $display_as_group, - ], 'radio' => [ '#type' => 'radio', '#title' => $category_info->getLabel(), @@ -221,13 +327,6 @@ public function buildForm(array $form, FormStateInterface $form_state, $entity_t '#attributes' => [ 'class' => ['field-option-radio'], ], - '#ajax' => [ - 'callback' => [$this, 'showFieldsCallback'], - 'event' => 'updateOptions', - 'wrapper' => 'group-field-options-wrapper', - 'progress' => 'none', - 'disable-refocus' => TRUE, - ], '#description' => [ '#type' => 'container', '#attributes' => [ @@ -244,7 +343,21 @@ public function buildForm(array $form, FormStateInterface $form_state, $entity_t } } uasort($field_type_options_radios, [SortArray::class, 'sortByWeightProperty']); + + $form['add-label'] = [ + '#type' => 'label', + '#title' => $this->t('Choose a type of field'), + '#required' => TRUE, + ]; + + $form['add'] = [ + '#type' => 'container', + '#attributes' => [ + 'class' => 'add-field-container', + ], + ]; $form['add']['new_storage_type'] = $field_type_options_radios; + $form['group_submit'] = [ '#type' => 'submit', '#value' => $this->t('Change field group'), @@ -252,135 +365,72 @@ public function buildForm(array $form, FormStateInterface $form_state, $entity_t '#attributes' => [ 'class' => ['js-hide'], ], - '#submit' => [[static::class, 'showFieldsHandler']], - ]; - $form['group_field_options_wrapper'] = [ - '#prefix' => '<div id="group-field-options-wrapper" class="group-field-options-wrapper">', - '#suffix' => '</div>', + '#submit' => [[static::class, 'rebuildWithOptions']], ]; - // Set the selected field to the form state by checking - // the checked attribute. - $selected_field_type = NULL; - foreach ($field_type_options_radios as $field_type_options_radio) { - if ($field_type_options_radio['#attributes']['checked']) { - $selected_field_type = $field_type_options_radio['radio']['#return_value']; - $form_state->setValue('selected_field_type', $selected_field_type); - break; - } - } - if (isset($selected_field_type)) { - $group_display = $field_type_options_radios[$selected_field_type]['#data']['#group_display']; - if ($group_display) { - $form['group_field_options_wrapper']['label'] = [ - '#type' => 'label', - '#title' => t('Choose an option below'), - '#required' => TRUE, - ]; - $form['group_field_options_wrapper']['fields'] = [ - '#type' => 'container', - '#attributes' => [ - 'class' => ['group-field-options'], - ], - ]; + $form['actions']['submit']['#validate'][] = '::validateGroupOrField'; + $form['actions']['submit']['#submit'][] = '::rebuildWithOptions'; + } - foreach ($unique_definitions[$selected_field_type] as $option_key => $option) { - $radio_element = [ - '#type' => 'radio', - '#theme_wrappers' => ['form_element__new_storage_type'], - '#title' => $option['label'], - '#description' => [ - '#theme' => 'item_list', - '#items' => $unique_definitions[$selected_field_type][$option_key]['description'], - ], - '#id' => $option['unique_identifier'], - '#weight' => $option['weight'], - '#parents' => ['group_field_options_wrapper'], - '#attributes' => [ - 'class' => ['field-option-radio'], - 'data-once' => 'field-click-to-select', - ], - '#wrapper_attributes' => [ - 'class' => ['js-click-to-select', 'subfield-option'], - ], - '#variant' => 'field-suboption', - ]; - $radio_element['#return_value'] = $option['unique_identifier']; - if ((string) $option['unique_identifier'] === 'entity_reference') { - $radio_element['#title'] = 'Other'; - $radio_element['#weight'] = 10; - } - $group_field_options[$option['unique_identifier']] = $radio_element; + /** + * Save field type definitions and categories in the form state. + * + * Get all field type definitions and store each one twice: + * - field_type_options: each field type is indexed by its category plugin ID + * or its label. + * - unique_definitions: each field type is indexed by its category and name. + * + * @param \Drupal\Core\Form\FormStateInterface $form_state + * The current state of the form. + */ + protected function processFieldDefinitions(FormStateInterface $form_state): void { + $field_type_options = $unique_definitions = []; + $grouped_definitions = $this->fieldTypePluginManager->getGroupedDefinitions($this->fieldTypePluginManager->getEntityTypeUiDefinitions($this->entityTypeId), 'label', 'id'); + // Invoke a hook to get category properties. + foreach ($grouped_definitions as $category => $field_types) { + foreach ($field_types as $name => $field_type) { + $definition = ['unique_identifier' => $name] + $field_type; + $category_info = $this->fieldTypeCategoryManager + ->createInstance($field_type['category'], $definition); + $definition['display_as_group'] = !($category_info instanceof FallbackFieldTypeCategory); + if ($this->fieldTypeCategoryManager->hasDefinition($category)) { + $id = $category_info->getPluginId(); + } + else { + $id = (string) $field_type['label']; } - uasort($group_field_options, [SortArray::class, 'sortByWeightProperty']); - $form['group_field_options_wrapper']['fields'] += $group_field_options; + $field_type_options[$id] = $definition; + $unique_definitions[$category][$name] = $definition; } } - $field_prefix = $this->config('field_ui.settings')->get('field_prefix'); - $form['new_storage_wrapper']['field_name'] = [ - '#type' => 'machine_name', - '#field_prefix' => $field_prefix, - '#size' => 15, - '#description' => $this->t('A unique machine-readable name containing letters, numbers, and underscores.'), - // Calculate characters depending on the length of the field prefix - // setting. Maximum length is 32. - '#maxlength' => FieldStorageConfig::NAME_MAX_LENGTH - strlen($field_prefix), - '#machine_name' => [ - 'source' => ['new_storage_wrapper', 'label'], - 'exists' => [$this, 'fieldNameExists'], - ], - '#required' => FALSE, - ]; - // Place the 'translatable' property as an explicit value so that contrib - // modules can form_alter() the value for newly created fields. By default - // we create field storage as translatable so it will be possible to enable - // translation at field level. - $form['translatable'] = [ - '#type' => 'value', - '#value' => TRUE, - ]; - - $form['actions'] = ['#type' => 'actions']; - $form['actions']['submit'] = [ - '#type' => 'submit', - '#value' => $this->t('Continue'), - '#button_type' => 'primary', - ]; - $form['#attached']['library'] = [ - 'field_ui/drupal.field_ui', - 'field_ui/drupal.field_ui.manage_fields', - 'core/drupal.ajax', - ]; - return $form; + $form_state->set('field_type_options', $field_type_options); + $form_state->set('unique_definitions', $unique_definitions); } /** - * {@inheritdoc} + * Validates the first step of the form. + * + * @param array $form + * An associative array containing the structure of the form. + * @param \Drupal\Core\Form\FormStateInterface $form_state + * The current state of the form. */ - public function validateForm(array &$form, FormStateInterface $form_state) { + public function validateGroupOrField(array &$form, FormStateInterface $form_state) { if (!$form_state->getValue('new_storage_type')) { - $form_state->setErrorByName('new_storage_type', $this->t('You need to select a field type.')); - } - elseif (isset($form['group_field_options_wrapper']['fields']) && !$form_state->getValue('group_field_options_wrapper')) { - $form_state->setErrorByName('group_field_options_wrapper', $this->t('You need to select a field type.')); + $form_state->setErrorByName('add', $this->t('You need to select a field type.')); } - - $this->validateAddNew($form, $form_state); } /** - * Validates the 'add new field' case. + * Validates the second step (field storage selection and label) of the form. * * @param array $form * An associative array containing the structure of the form. * @param \Drupal\Core\Form\FormStateInterface $form_state * The current state of the form. - * - * @see \Drupal\field_ui\Form\FieldStorageAddForm::validateForm() */ - protected function validateAddNew(array $form, FormStateInterface $form_state) { - // Validate if any information was provided in the 'add new field' case. + public function validateAddNew(array $form, FormStateInterface $form_state) { // Missing label. if (!$form_state->getValue('label')) { $form_state->setErrorByName('label', $this->t('Add new field: you need to provide a label.')); @@ -397,6 +447,10 @@ protected function validateAddNew(array $form, FormStateInterface $form_state) { $field_name = $this->configFactory->get('field_ui.settings')->get('field_prefix') . $field_name; $form_state->setValueForElement($form['new_storage_wrapper']['field_name'], $field_name); } + + if (isset($form['group_field_options_wrapper']['fields']) && !$form_state->getValue('group_field_options_wrapper')) { + $form_state->setErrorByName('group_field_options_wrapper', $this->t('You need to choose an option.')); + } } /** @@ -554,16 +608,17 @@ public function fieldNameExists($value, $element, FormStateInterface $form_state } /** - * Callback for displaying fields after a group has been selected. + * Submit handler for displaying fields after a group is selected. */ - public function showFieldsCallback($form, FormStateInterface &$form_state) { - return $form['group_field_options_wrapper']; + public static function rebuildWithOptions($form, FormStateInterface &$form_state) { + $form_state->setRebuild(); } /** - * Submit handler for displaying fields after a group is selected. + * Submit handler for resetting the form. */ - public static function showFieldsHandler($form, FormStateInterface &$form_state) { + public static function startOver($form, FormStateInterface &$form_state) { + $form_state->unsetValue('new_storage_type'); $form_state->setRebuild(); } diff --git a/core/modules/field_ui/tests/src/Functional/ManageFieldsFunctionalTest.php b/core/modules/field_ui/tests/src/Functional/ManageFieldsFunctionalTest.php index eb2da05b3eae..aed84bcc2859 100644 --- a/core/modules/field_ui/tests/src/Functional/ManageFieldsFunctionalTest.php +++ b/core/modules/field_ui/tests/src/Functional/ManageFieldsFunctionalTest.php @@ -115,23 +115,27 @@ public function testDisallowedFieldNames() { $this->config('field_ui.settings')->set('field_prefix', '')->save(); $label = 'Disallowed field'; - $edit = [ - 'label' => $label, + $edit1 = [ 'new_storage_type' => 'test_field', ]; + $edit2 = [ + 'label' => $label, + ]; // Try with an entity key. - $edit['field_name'] = 'title'; + $edit2['field_name'] = 'title'; $bundle_path = 'admin/structure/types/manage/' . $this->contentType; $this->drupalGet("{$bundle_path}/fields/add-field"); - $this->submitForm($edit, 'Continue'); + $this->submitForm($edit1, 'Continue'); + $this->submitForm($edit2, 'Continue'); $this->assertSession()->pageTextContains('The machine-readable name is already in use. It must be unique.'); // Try with a base field. - $edit['field_name'] = 'sticky'; + $edit2['field_name'] = 'sticky'; $bundle_path = 'admin/structure/types/manage/' . $this->contentType; $this->drupalGet("{$bundle_path}/fields/add-field"); - $this->submitForm($edit, 'Continue'); + $this->submitForm($edit1, 'Continue'); + $this->submitForm($edit2, 'Continue'); $this->assertSession()->pageTextContains('The machine-readable name is already in use. It must be unique.'); } @@ -219,9 +223,13 @@ public function testHiddenFields() { ->elementExists('css', "[name='new_storage_type'][value='$field_type']"); } catch (ElementNotFoundException) { - if ($this->getFieldFromGroup($field_type)) { + if ($group = $this->getFieldFromGroup($field_type)) { + $this->assertSession() + ->elementExists('css', "[name='new_storage_type'][value='$group']"); + $this->submitForm(['new_storage_type' => $group], 'Continue'); $this->assertSession() ->elementExists('css', "[name='group_field_options_wrapper'][value='$field_type']"); + $this->submitForm([], 'Back'); } } } @@ -239,10 +247,13 @@ public function testDuplicateFieldName() { // create a new field with the same name. $url = 'admin/structure/types/manage/' . $this->contentType . '/fields/add-field'; $this->drupalGet($url); + $edit = [ + 'new_storage_type' => 'boolean', + ]; + $this->submitForm($edit, 'Continue'); $edit = [ 'label' => $this->randomMachineName(), 'field_name' => 'tags', - 'new_storage_type' => 'boolean', ]; $this->submitForm($edit, 'Continue'); @@ -384,12 +395,16 @@ public function testFieldPrefix() { $field_exceed_max_length_input = $this->randomMachineName(23); // Try to create the field. - $edit = [ + $edit1 = [ + 'new_storage_type' => 'test_field', + ]; + $edit2 = [ 'label' => $field_exceed_max_length_label, 'field_name' => $field_exceed_max_length_input, ]; $this->drupalGet('admin/structure/types/manage/' . $this->contentType . '/fields/add-field'); - $this->submitForm($edit, 'Continue'); + $this->submitForm($edit1, 'Continue'); + $this->submitForm($edit2, 'Continue'); $this->assertSession()->pageTextContains('Machine-readable name cannot be longer than 22 characters but is currently 23 characters long.'); // Create a valid field. diff --git a/core/modules/field_ui/tests/src/Functional/ManageFieldsTest.php b/core/modules/field_ui/tests/src/Functional/ManageFieldsTest.php index 8bd98ebe26f9..35d8d9ad21dd 100644 --- a/core/modules/field_ui/tests/src/Functional/ManageFieldsTest.php +++ b/core/modules/field_ui/tests/src/Functional/ManageFieldsTest.php @@ -142,10 +142,13 @@ public function testAddField() { $this->assertNull(FieldStorageConfig::loadByName('node', "field_test_field")); $this->drupalGet('/admin/structure/types/manage/' . $type->id() . '/fields/add-field'); + $edit = [ + 'new_storage_type' => 'test_field', + ]; + $this->submitForm($edit, 'Continue'); $edit = [ 'label' => 'Test field', 'field_name' => 'test_field', - 'new_storage_type' => 'test_field', ]; $this->submitForm($edit, 'Continue'); $this->assertSession()->statusMessageNotContains('Saved'); @@ -160,10 +163,13 @@ public function testAddField() { // Try creating a field with the same machine name. $this->drupalGet('/admin/structure/types/manage/' . $type->id() . '/fields/add-field'); + $edit = [ + 'new_storage_type' => 'test_field', + ]; + $this->submitForm($edit, 'Continue'); $edit = [ 'label' => 'Test field', 'field_name' => 'test_field', - 'new_storage_type' => 'test_field', ]; $this->submitForm($edit, 'Continue'); // Assert that the values in the field storage form are reset. @@ -193,10 +199,13 @@ public function testAddFieldWithMultipleUsers() { // Start adding a field as user 1, stop prior to saving, but keep the URL. $this->drupalLogin($user1); $this->drupalGet($bundle_path . '/fields/add-field'); + $edit = [ + 'new_storage_type' => 'test_field', + ]; + $this->submitForm($edit, 'Continue'); $edit = [ 'label' => 'Test field', 'field_name' => 'test_field', - 'new_storage_type' => 'test_field', ]; $this->submitForm($edit, 'Continue'); // Make changes to the storage form. @@ -208,10 +217,13 @@ public function testAddFieldWithMultipleUsers() { // Actually add a field as user 2. $this->drupalLogin($user2); $this->drupalGet($bundle_path . '/fields/add-field'); + $edit = [ + 'new_storage_type' => 'test_field', + ]; + $this->submitForm($edit, 'Continue'); $edit = [ 'label' => 'Test field', 'field_name' => 'test_field', - 'new_storage_type' => 'test_field', ]; $this->submitForm($edit, 'Continue'); $allowed_no_of_values = $page->findField('field_storage[subform][cardinality_number]')->getValue(); @@ -246,10 +258,13 @@ public function testEditFieldWithLeftOverFieldInTempStore() { // Start adding a field but stop prior to saving. $this->drupalLogin($user); $this->drupalGet($bundle_path . '/fields/add-field'); + $edit = [ + 'new_storage_type' => 'test_field', + ]; + $this->submitForm($edit, 'Continue'); $edit = [ 'label' => 'Test field', 'field_name' => 'test_field', - 'new_storage_type' => 'test_field', ]; $this->submitForm($edit, 'Continue'); diff --git a/core/modules/field_ui/tests/src/FunctionalJavascript/ManageFieldsTest.php b/core/modules/field_ui/tests/src/FunctionalJavascript/ManageFieldsTest.php index a33d124dee84..36fca849dc6f 100644 --- a/core/modules/field_ui/tests/src/FunctionalJavascript/ManageFieldsTest.php +++ b/core/modules/field_ui/tests/src/FunctionalJavascript/ManageFieldsTest.php @@ -179,38 +179,40 @@ public function testAddField() { $assert_session = $this->assertSession(); $this->drupalGet('admin/structure/types/manage/article/fields/add-field'); - $field_name = 'test_field_1'; - $page->fillField('label', $field_name); // Test validation. $page->pressButton('Continue'); $assert_session->pageTextContains('You need to select a field type.'); - $assert_session->elementExists('css', '[name="new_storage_type"].error'); $assert_session->pageTextNotContains('Choose an option below'); $this->assertNotEmpty($number_field = $page->find('xpath', '//*[text() = "Number"]')->getParent()); $number_field->click(); - $assert_session->assertWaitOnAjaxRequest(); $this->assertTrue($assert_session->elementExists('css', '[name="new_storage_type"][value="number"]')->isSelected()); + $page->pressButton('Continue'); $assert_session->pageTextContains('Choose an option below'); + $field_name = 'test_field_1'; + $page->fillField('label', $field_name); $page->pressButton('Continue'); - $assert_session->pageTextContains('You need to select a field type.'); + $assert_session->pageTextContains('You need to choose an option.'); $assert_session->elementNotExists('css', '[name="new_storage_type"].error'); $assert_session->elementExists('css', '[name="group_field_options_wrapper"].error'); + $page->pressButton('Back'); // Try adding a field using a grouped field type. $this->assertNotEmpty($email_field = $page->find('xpath', '//*[text() = "Email"]')->getParent()); $email_field->click(); - $assert_session->assertWaitOnAjaxRequest(); $this->assertTrue($assert_session->elementExists('css', '[name="new_storage_type"][value="email"]')->isSelected()); + $page->pressButton('Continue'); $assert_session->pageTextNotContains('Choose an option below'); + $page->pressButton('Back'); $this->assertNotEmpty($text = $page->find('xpath', '//*[text() = "Plain text"]')->getParent()); $text->click(); - $assert_session->assertWaitOnAjaxRequest(); $this->assertTrue($assert_session->elementExists('css', '[name="new_storage_type"][value="plain_text"]')->isSelected()); + $page->pressButton('Continue'); $assert_session->pageTextContains('Choose an option below'); + $page->fillField('label', $field_name); $this->assertNotEmpty($text_plain = $page->find('xpath', '//*[text() = "Text (plain)"]')->getParent()); $text_plain->click(); $this->assertTrue($assert_session->elementExists('css', '[name="group_field_options_wrapper"][value="string"]')->isSelected()); @@ -265,22 +267,23 @@ public function testAddField() { // Try adding a field using a non-grouped field type. $this->drupalGet('admin/structure/types/manage/article/fields/add-field'); - $field_name = 'test_field_2'; - $page->fillField('label', $field_name); $this->assertNotEmpty($number_field = $page->find('xpath', '//*[text() = "Number"]')->getParent()); $number_field->click(); - $assert_session->assertWaitOnAjaxRequest(); $this->assertTrue($assert_session->elementExists('css', '[name="new_storage_type"][value="number"]')->isSelected()); + $page->pressButton('Continue'); $assert_session->pageTextContains('Choose an option below'); $this->assertNotEmpty($number_integer = $page->find('xpath', '//*[text() = "Number (integer)"]')->getParent()); $number_integer->click(); $this->assertTrue($assert_session->elementExists('css', '[name="group_field_options_wrapper"][value="integer"]')->isSelected()); + $page->pressButton('Back'); $this->assertNotEmpty($test_field = $page->find('xpath', '//*[text() = "Test field"]')->getParent()); $test_field->click(); - $assert_session->assertWaitOnAjaxRequest(); $this->assertTrue($assert_session->elementExists('css', '[name="new_storage_type"][value="test_field"]')->isSelected()); + $page->pressButton('Continue'); + $field_name = 'test_field_2'; + $page->fillField('label', $field_name); $assert_session->pageTextNotContains('Choose an option below'); $page->pressButton('Continue'); @@ -305,7 +308,7 @@ public function testFieldTypeOrder() { // Select the group card. $group_field_card = $page->find('css', "[name='new_storage_type'][value='$field_type_category']")->getParent(); $group_field_card->click(); - $this->assertSession()->assertWaitOnAjaxRequest(); + $page->pressButton('Continue'); $field_types = $page->findAll('css', '.subfield-option .option'); $field_type_labels = []; foreach ($field_types as $field_type) { @@ -325,6 +328,8 @@ public function testFieldTypeOrder() { }; // Assert that the field type options are displayed as per their weights. $this->assertSame($expected_field_types, $field_type_labels); + // Return to the first step of the form. + $page->pressButton('Back'); } } @@ -356,10 +361,15 @@ public function testAllowedValuesFormValidation() { public function testLabelFieldFormValidation() { $this->drupalGet('/admin/structure/types/manage/article/fields/add-field'); $page = $this->getSession()->getPage(); - $page->findButton('Continue')->click(); - $this->assertSession()->pageTextContains('You need to provide a label.'); + $page->findButton('Continue')->click(); $this->assertSession()->pageTextContains('You need to select a field type.'); + + $this->assertNotEmpty($boolean_field = $page->find('xpath', '//*[text() = "Boolean (overridden by alter)"]')->getParent()); + $boolean_field->click(); + $page->findButton('Continue')->click(); + $page->findButton('Continue')->click(); + $this->assertSession()->pageTextContains('Add new field: you need to provide a label.'); } } diff --git a/core/modules/field_ui/tests/src/Traits/FieldUiJSTestTrait.php b/core/modules/field_ui/tests/src/Traits/FieldUiJSTestTrait.php index cba90a5bb4bb..c611e00adede 100644 --- a/core/modules/field_ui/tests/src/Traits/FieldUiJSTestTrait.php +++ b/core/modules/field_ui/tests/src/Traits/FieldUiJSTestTrait.php @@ -51,6 +51,7 @@ public function fieldUIAddNewFieldJS(?string $bundle_path, string $field_name, ? $field_card = $this->getFieldFromGroupJS($field_type); } $field_card?->click(); + $page->findButton('Continue')->click(); $field_label = $page->findField('edit-label'); $this->assertTrue($field_label->isVisible()); $field_label = $page->find('css', 'input[data-drupal-selector="edit-label"]'); @@ -140,11 +141,12 @@ public function getFieldFromGroupJS($field_type) { foreach ($groups as $group) { $group_field_card = $this->getSession()->getPage()->find('css', "[name='new_storage_type'][value='$group']")->getParent(); $group_field_card->click(); - $this->assertSession()->assertWaitOnAjaxRequest(); + $this->getSession()->getPage()->pressButton('Continue'); $field_card = $this->getSession()->getPage()->find('css', "[name='group_field_options_wrapper'][value='$field_type']"); if ($field_card) { break; } + $this->getSession()->getPage()->pressButton('Back'); } return $field_card->getParent(); } diff --git a/core/modules/field_ui/tests/src/Traits/FieldUiTestTrait.php b/core/modules/field_ui/tests/src/Traits/FieldUiTestTrait.php index 87e835f7cead..5a86e52f846f 100644 --- a/core/modules/field_ui/tests/src/Traits/FieldUiTestTrait.php +++ b/core/modules/field_ui/tests/src/Traits/FieldUiTestTrait.php @@ -38,7 +38,13 @@ public function fieldUIAddNewField($bundle_path, $field_name, $label = NULL, $fi // test failure. // See https://www.drupal.org/project/drupal/issues/3030902 $label = $label ?: $this->randomMachineName(); - $initial_edit = []; + $initial_edit = [ + 'new_storage_type' => $field_type, + ]; + $second_edit = [ + 'label' => $label, + 'field_name' => $field_name, + ]; // Allow the caller to set a NULL path in case they navigated to the right // page before calling this method. @@ -54,12 +60,6 @@ public function fieldUIAddNewField($bundle_path, $field_name, $label = NULL, $fi try { // First check if the passed in field type is not part of a group. $this->assertSession()->elementExists('css', "[name='new_storage_type'][value='$field_type']"); - // If the element exists then we can add it to our object. - $initial_edit = [ - 'new_storage_type' => $field_type, - 'label' => $label, - 'field_name' => $field_name, - ]; } // If the element could not be found then it is probably in a group. catch (ElementNotFoundException) { @@ -67,18 +67,13 @@ public function fieldUIAddNewField($bundle_path, $field_name, $label = NULL, $fi $field_group = $this->getFieldFromGroup($field_type); if ($field_group) { // Pass in the group name as the new storage type. - $selected_group = [ - 'new_storage_type' => $field_group, - ]; - $this->submitForm($selected_group, 'Change field group'); - $initial_edit = [ - 'group_field_options_wrapper' => $field_type, - 'label' => $label, - 'field_name' => $field_name, - ]; + $initial_edit['new_storage_type'] = $field_group; + $second_edit['group_field_options_wrapper'] = $field_type; + $this->drupalGet($bundle_path); } } $this->submitForm($initial_edit, 'Continue'); + $this->submitForm($second_edit, 'Continue'); // Assert that the field is not created. $this->assertFieldDoesNotExist($bundle_path, $label); if ($save_settings) { @@ -208,12 +203,14 @@ public function getFieldFromGroup($field_type) { $test = [ 'new_storage_type' => $group, ]; - $this->submitForm($test, 'Change field group'); + $this->submitForm($test, 'Continue'); try { $this->assertSession()->elementExists('css', "[name='group_field_options_wrapper'][value='$field_type']"); + $this->submitForm([], 'Back'); return $group; } catch (ElementNotFoundException) { + $this->submitForm([], 'Back'); continue; } } diff --git a/core/modules/media/media.module b/core/modules/media/media.module index 595389ab8476..b0ba7843f199 100644 --- a/core/modules/media/media.module +++ b/core/modules/media/media.module @@ -208,7 +208,7 @@ function media_form_field_ui_field_storage_add_form_alter(&$form, FormStateInter 'file_upload', 'field_ui:entity_reference:media', ]; - if (in_array($form_state->getValue('selected_field_type'), $field_types)) { + if (in_array($form_state->getValue('new_storage_type'), $field_types)) { $form['group_field_options_wrapper']['description_wrapper'] = [ '#type' => 'item', '#markup' => $description_text, diff --git a/core/modules/media/tests/src/FunctionalJavascript/MediaReferenceFieldHelpTest.php b/core/modules/media/tests/src/FunctionalJavascript/MediaReferenceFieldHelpTest.php index 2b351a002fe9..4f1cf6d2bf5a 100644 --- a/core/modules/media/tests/src/FunctionalJavascript/MediaReferenceFieldHelpTest.php +++ b/core/modules/media/tests/src/FunctionalJavascript/MediaReferenceFieldHelpTest.php @@ -49,8 +49,9 @@ public function testFieldCreationHelpText() { // visible. $assert_session->elementExists('css', "[name='new_storage_type'][value='boolean']"); $page->find('css', "[name='new_storage_type'][value='boolean']")->getParent()->click(); - $assert_session->assertWaitOnAjaxRequest(); + $page->pressButton('Continue'); $assert_session->pageTextNotContains($help_text); + $page->pressButton('Back'); // Select each of the Reference, File upload field groups and verify their // descriptions are now visible and match the expected text. @@ -58,8 +59,9 @@ public function testFieldCreationHelpText() { $assert_session->elementExists('css', "[name='new_storage_type'][value='$field_group']"); $page->find('css', "[name='new_storage_type'][value='$field_group']")->getParent()->click(); - $assert_session->assertWaitOnAjaxRequest(); + $page->pressButton('Continue'); $assert_session->pageTextContains($help_text); + $page->pressButton('Back'); } } diff --git a/core/modules/media_library/tests/src/FunctionalJavascript/FieldUiIntegrationTest.php b/core/modules/media_library/tests/src/FunctionalJavascript/FieldUiIntegrationTest.php index c2584dfcbe4b..f1f08d1f4542 100644 --- a/core/modules/media_library/tests/src/FunctionalJavascript/FieldUiIntegrationTest.php +++ b/core/modules/media_library/tests/src/FunctionalJavascript/FieldUiIntegrationTest.php @@ -69,6 +69,7 @@ public function testFieldUiIntegration() { $this->drupalGet('/admin/structure/types/manage/article/fields/add-field'); $page->find('css', "[name='new_storage_type'][value='field_ui:entity_reference:media']")->getParent()->click(); + $page->findButton('Continue')->click(); $this->assertNotNull($assert_session->waitForField('label')); $page->fillField('label', 'Shatner'); $this->waitForText('field_shatner'); diff --git a/core/modules/node/tests/src/Functional/NodeTypeTranslationTest.php b/core/modules/node/tests/src/Functional/NodeTypeTranslationTest.php index 043cdb80b4d5..0f6a7149cc26 100644 --- a/core/modules/node/tests/src/Functional/NodeTypeTranslationTest.php +++ b/core/modules/node/tests/src/Functional/NodeTypeTranslationTest.php @@ -170,6 +170,8 @@ public function testNodeTypeTitleLabelTranslation() { $this->drupalGet("admin/structure/types/manage/{$type}/fields/add-field"); $this->submitForm([ 'new_storage_type' => 'email', + ], 'Continue'); + $this->submitForm([ 'label' => 'Email', 'field_name' => 'email', ], 'Continue'); -- GitLab