From d772fbb00bc99ab6c94564ce660731db78d7db1b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=CC=81bor=20Hojtsy?= <gabor@hojtsy.hu> Date: Mon, 18 Mar 2019 11:56:39 +0100 Subject: [PATCH] Issue #3023801 by seanB, DamienMcKenna, lauriii, phenaproxima, Berdir, Pancho, marcoscano, Wim Leers, rainbreaw, larowlan, webchick, jrockowitz, andrewmacpherson, ckrina: Allow newly uploaded files to be deleted from the media library without saving them --- core/misc/icons/ee0000/ex.svg | 1 + .../media_library/css/media_library.theme.css | 148 ++++++++++++++--- .../media_library/src/Form/AddFormBase.php | 155 ++++++++++++++++-- .../media_library/src/Form/FileUploadForm.php | 14 +- .../media_library/src/Form/OEmbedForm.php | 3 +- .../FunctionalJavascript/MediaLibraryTest.php | 114 ++++++++++++- .../src/Kernel/MediaLibraryAddFormTest.php | 4 +- 7 files changed, 394 insertions(+), 45 deletions(-) create mode 100644 core/misc/icons/ee0000/ex.svg diff --git a/core/misc/icons/ee0000/ex.svg b/core/misc/icons/ee0000/ex.svg new file mode 100644 index 000000000000..6b45a1d5726e --- /dev/null +++ b/core/misc/icons/ee0000/ex.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><path fill="#ee0000" d="M3.51 13.925c.194.194.512.195.706.001l3.432-3.431c.194-.194.514-.194.708 0l3.432 3.431c.192.194.514.193.707-.001l1.405-1.417c.191-.195.189-.514-.002-.709l-3.397-3.4c-.192-.193-.192-.514-.002-.708l3.401-3.43c.189-.195.189-.515 0-.709l-1.407-1.418c-.195-.195-.513-.195-.707-.001l-3.43 3.431c-.195.194-.516.194-.708 0l-3.432-3.431c-.195-.195-.512-.194-.706.001l-1.407 1.417c-.194.195-.194.515 0 .71l3.403 3.429c.193.195.193.514-.001.708l-3.4 3.399c-.194.195-.195.516-.001.709l1.406 1.419z"/></svg> diff --git a/core/modules/media_library/css/media_library.theme.css b/core/modules/media_library/css/media_library.theme.css index 2bad77cefb3d..0e066d888748 100644 --- a/core/modules/media_library/css/media_library.theme.css +++ b/core/modules/media_library/css/media_library.theme.css @@ -27,8 +27,10 @@ margin: 0; } -/* @todo Use a class instead of the li element. - https://www.drupal.org/project/drupal/issues/3029227 */ +/** + * @todo Use a class instead of the li element. + * https://www.drupal.org/project/drupal/issues/3029227 + */ .media-library-menu li { display: block; } @@ -84,17 +86,27 @@ } /* Generic media add form styles. */ -.media-library-add-form--without-input { +.media-library-add-form--without-input .form-item { + margin: 0 0 1em; +} + +/** + * Remove outline from added media container. + * + * The added media container receives focus after adding new media, but since + * it is not an interactive element it does not need an outline. + */ +.media-library-add-form__added-media { + outline: none; +} + +.media-library-add-form__input-wrapper { padding: 16px; border: 1px solid #bfbfbf; border-radius: 2px; background: #fcfcfa; } -.media-library-add-form--without-input .form-item { - margin: 0 0 1em; -} - /* Style the media add upload form. */ .media-library-add-form--upload.media-library-add-form--without-input .form-item-upload { margin-bottom: 0; @@ -105,12 +117,12 @@ } /* Style the media add oEmbed form. */ -.media-library-add-form-oembed-wrapper { +.media-library-add-form--oembed .media-library-add-form__input-wrapper { display: flex; } @media screen and (max-width: 37.5em) { - .media-library-add-form-oembed-wrapper { + .media-library-add-form--oembed .media-library-add-form__input-wrapper { display: block; } } @@ -123,8 +135,10 @@ width: 100%; } -/* @todo Remove .button when styles are moved to the seven theme in - https://www.drupal.org/project/drupal/issues/2980769 */ +/** + * @todo Remove .button when styles are moved to the seven theme in + * https://www.drupal.org/project/drupal/issues/2980769 + */ .button.media-library-add-form-oembed-submit { align-self: center; } @@ -152,8 +166,13 @@ margin: 0.75em 0; } -/* Override the table display of the visually hidden labels so they won't take - up space. */ +/** + * Override the table display of the visually hidden labels. + * + * The width, height and overflow properties in the styles for the + * .visually-hidden class do not work correctly if the element has a table + * display. + */ .media-library-item label { display: inline-block; } @@ -166,8 +185,10 @@ justify-content: space-between; } -/* @todo Remove order and reorder the views header and filters via a views - template in https://www.drupal.org/project/drupal/issues/3035994 */ +/** + * @todo Remove order and reorder the views header and filters via a views + * template in https://www.drupal.org/project/drupal/issues/3035994 + */ .media-library-wrapper .view-header { order: 2; align-self: flex-end; @@ -178,21 +199,27 @@ text-align: left; } -/* @todo Remove order and reorder the views header and filters via a views - template in https://www.drupal.org/project/drupal/issues/3035994 */ +/** + * @todo Remove order and reorder the views header and filters via a views + * template in https://www.drupal.org/project/drupal/issues/3035994 + */ .media-library-wrapper .media-library-view .view-filters { order: 1; } -/* @todo Remove order and reorder the views header and filters via a views - template in https://www.drupal.org/project/drupal/issues/3035994 */ +/** + * @todo Remove order and reorder the views header and filters via a views + * template in https://www.drupal.org/project/drupal/issues/3035994 + */ .media-library-wrapper .media-library-view .view-content { flex: 0 0 100%; order: 3; } -/* @todo Remove order and reorder the views header and filters via a views - template in https://www.drupal.org/project/drupal/issues/3035994 */ +/** + * @todo Remove order and reorder the views header and filters via a views + * template in https://www.drupal.org/project/drupal/issues/3035994 + */ .media-library-wrapper .media-library-view .pager { order: 4; } @@ -403,8 +430,10 @@ position: relative; } -/* @todo Change to .media-library-open-button when styles are moved to the - seven theme in https://www.drupal.org/project/drupal/issues/2980769 */ +/** + * @todo Change to .media-library-open-button when styles are moved to the + * seven theme in https://www.drupal.org/project/drupal/issues/2980769 + */ .button.media-library-open-button { margin-left: 0; /* LTR */ } @@ -464,11 +493,19 @@ border-color: #40b6ff; } -/* Style the wrappers around new media and files. */ +/** + * Style the added media item container. + * + * The added media container receives screen reader focus since it is has the + * role 'listitem'. Since it is not an interactive element, it does not need + * an outline. + */ .media-library-add-form__media { + position: relative; display: flex; padding: 20px 0 20px 0; border-bottom: 1px solid #c0c0c0; + outline: none; } /* Do not show the top padding for the first item. */ @@ -476,6 +513,16 @@ padding-top: 0; } +/** + * Change the position of the remove button for the first item. + * + * The first item doesn't have a top padding, change the location of the remove + * button as well. + */ +.media-library-add-form__media:first-child .media-library-add-form__remove-button[type="submit"] { + top: 5px; +} + /* Do not show the bottom border and padding for the last item. */ .media-library-add-form__media:last-child { padding-bottom: 0; @@ -504,6 +551,59 @@ margin-left: 20px; } +/** + * @todo Remove [type="submit"] when styles are moved to the seven theme in + * https://www.drupal.org/project/drupal/issues/2980769 + */ +.media-library-add-form__remove-button[type="submit"] { + position: absolute; + top: 25px; + right: 6px; /* LTR */ + width: auto; + margin: 0; + padding: 2px 20px 2px 2px; /* LTR */ + text-transform: lowercase; + color: transparent; + border: 0; + border-radius: 0; + background: transparent url(../../../misc/icons/787878/ex.svg) right 2px no-repeat; /* LTR */ + font-weight: normal; + line-height: 16px; +} +[dir="rtl"] .media-library-add-form__remove-button[type="submit"] { + right: auto; + left: 13px; + padding: 2px 2px 2px 20px; + background-position: left 2px; +} + +.media-library-add-form__remove-button:focus, +.media-library-add-form__remove-button.button:disabled, +.media-library-add-form__remove-button.button:disabled:active, +.media-library-add-form__remove-button.button:focus { + color: #787878; + border: 0; + background: transparent url(../../../misc/icons/787878/ex.svg) right 2px no-repeat; /* LTR */ +} +[dir="rtl"] .media-library-add-form__remove-button:focus, +[dir="rtl"] .media-library-add-form__remove-button.button:disabled, +[dir="rtl"] .media-library-add-form__remove-button.button:disabled:active, +[dir="rtl"] .media-library-add-form__remove-button.button:focus { + background-position: left 2px; +} + +.media-library-add-form__remove-button:hover, +.media-library-add-form__remove-button.button:hover { + color: #e00; + border: 0; + background: transparent url(../../../misc/icons/ee0000/ex.svg) right 2px no-repeat; /* LTR */ + box-shadow: none; +} +[dir="rtl"] .media-library-add-form__remove-button:hover, +[dir="rtl"] .media-library-add-form__remove-button.button:hover { + background-position: left 2px; +} + /* @todo Remove or re-work in https://www.drupal.org/node/2985168 */ .media-library-widget .media-library-item__name a, .media-library-view--widget .media-library-item__name a { diff --git a/core/modules/media_library/src/Form/AddFormBase.php b/core/modules/media_library/src/Form/AddFormBase.php index d6e76a5a03a1..b4ef7ad1e0d1 100644 --- a/core/modules/media_library/src/Form/AddFormBase.php +++ b/core/modules/media_library/src/Form/AddFormBase.php @@ -3,6 +3,7 @@ namespace Drupal\media_library\Form; use Drupal\Core\Ajax\AjaxResponse; +use Drupal\Core\Ajax\InvokeCommand; use Drupal\Core\Ajax\ReplaceCommand; use Drupal\Core\Entity\Entity\EntityFormDisplay; use Drupal\Core\Entity\EntityStorageInterface; @@ -109,7 +110,9 @@ protected function getMediaType(FormStateInterface $form_state) { * {@inheritdoc} */ public function buildForm(array $form, FormStateInterface $form_state) { - $form['#prefix'] = '<div id="media-library-add-form-wrapper">'; + // @todo Remove the ID when we can use selectors to replace content via + // AJAX in https://www.drupal.org/project/drupal/issues/2821793. + $form['#prefix'] = '<div id="media-library-add-form-wrapper" class="media-library-add-form-wrapper">'; $form['#suffix'] = '</div>'; $form['#attached']['library'][] = 'media_library/style'; @@ -139,6 +142,19 @@ public function buildForm(array $form, FormStateInterface $form_state) { $form['media'] = [ '#type' => 'container', + '#attributes' => [ + 'class' => [ + 'media-library-add-form__added-media', + ], + 'aria-label' => $this->t('Added media items'), + 'role' => 'list', + // Add the tabindex '-1' to allow the focus to be shifted to the added + // media wrapper when items are added. We set focus to the container + // because a media item does not necessarily have required fields and + // we do not want to set focus to the remove button automatically. + // @see ::updateFormCallback() + 'tabindex' => '-1', + ], ]; foreach ($added_media as $delta => $media) { @@ -185,15 +201,36 @@ abstract protected function buildInputElement(array $form, FormStateInterface $f * The element containing the required fields sub-form. */ protected function buildEntityFormElement(MediaInterface $media, array $form, FormStateInterface $form_state, $delta) { + // We need to make sure each button has a unique name attribute. The default + // name for button elements is 'op'. If the name is not unique, the + // triggering element is not set correctly and the wrong media item is + // removed. + // @see ::removeButtonSubmit() + $parents = $form['#parents']; + $id_suffix = $parents ? '-' . implode('-', $parents) : ''; + $element = [ '#type' => 'container', '#attributes' => [ 'class' => [ 'media-library-add-form__media', ], + 'aria-label' => $media->getName(), + 'role' => 'listitem', + // Add the tabindex '-1' to allow the focus to be shifted to the next + // media item when an item is removed. We set focus to the container + // because a media item does not necessarily have required fields and we + // do not want to set focus to the remove button automatically. + // @see ::updateFormCallback() + 'tabindex' => '-1', + // Add a data attribute containing the delta to allow us to easily shift + // the focus to a specific media item. + // @see ::updateFormCallback() + 'data-media-library-added-delta' => $delta, ], 'preview' => [ '#type' => 'container', + '#weight' => 10, '#attributes' => [ 'class' => [ 'media-library-add-form__preview', @@ -202,6 +239,7 @@ protected function buildEntityFormElement(MediaInterface $media, array $form, Fo ], 'fields' => [ '#type' => 'container', + '#weight' => 20, '#attributes' => [ 'class' => [ 'media-library-add-form__fields', @@ -211,6 +249,24 @@ protected function buildEntityFormElement(MediaInterface $media, array $form, Fo // to build the entity form fields. '#parents' => ['media', $delta, 'fields'], ], + 'remove_button' => [ + '#type' => 'submit', + '#value' => $this->t('Remove'), + '#name' => 'media-' . $delta . '-remove-button' . $id_suffix, + '#weight' => 30, + '#attributes' => [ + 'class' => ['media-library-add-form__remove-button'], + 'aria-label' => $this->t('Remove @label', ['@label' => $media->getName()]), + ], + '#ajax' => [ + 'callback' => '::updateFormCallback', + 'wrapper' => 'media-library-add-form-wrapper', + 'message' => $this->t('Removing @label.', ['@label' => $media->getName()]), + ], + '#submit' => ['::removeButtonSubmit'], + // Ensure errors in other media items do not prevent removal. + '#limit_validation_errors' => [], + ], ]; // @todo Make the image style configurable in // https://www.drupal.org/node/2988223 @@ -295,7 +351,8 @@ protected function processInputValues(array $source_field_values, array $form, F $media = array_map(function ($source_field_value) use ($media_type, $media_storage, $source_field_name) { return $this->createMediaFromValue($media_type, $media_storage, $source_field_name, $source_field_value); }, $source_field_values); - $form_state->set('media', $media)->setRebuild(); + // Re-key the media items before setting them in the form state. + $form_state->set('media', array_values($media))->setRebuild(); } /** @@ -332,6 +389,33 @@ protected function prepareMediaEntityForSave(MediaInterface $media) { // Intentionally empty by default. } + /** + * Submit handler for the remove button. + * + * @param array $form + * The form render array. + * @param \Drupal\Core\Form\FormStateInterface $form_state + * The form state. + */ + public function removeButtonSubmit(array $form, FormStateInterface $form_state) { + // Retrieve the delta of the media item from the parents of the remove + // button. + $triggering_element = $form_state->getTriggeringElement(); + $delta = array_slice($triggering_element['#array_parents'], -2, 1)[0]; + + $added_media = $form_state->get('media'); + $removed_media = $added_media[$delta]; + + // Update the list of added media items in the form state. + unset($added_media[$delta]); + + // Update the media items in the form state. + $form_state->set('media', $added_media)->setRebuild(); + + // Show a message to the user to confirm the media is removed. + $this->messenger()->addStatus($this->t('The media item %label has been removed.', ['%label' => $removed_media->label()])); + } + /** * AJAX callback to update the entire form based on source field input. * @@ -344,17 +428,57 @@ protected function prepareMediaEntityForSave(MediaInterface $media) { * The form render array or an AJAX response object. */ public function updateFormCallback(array &$form, FormStateInterface $form_state) { + $triggering_element = $form_state->getTriggeringElement(); + $wrapper_id = $triggering_element['#ajax']['wrapper']; + $added_media = $form_state->get('media'); + + $response = new AjaxResponse(); + // When the source field input contains errors, replace the existing form to // let the user change the source field input. If the user input is valid, // the entire modal is replaced with the second step of the form to show the // form fields for each media item. if ($form_state::hasAnyErrors()) { - $response = new AjaxResponse(); $response->addCommand(new ReplaceCommand('#media-library-add-form-wrapper', $form)); return $response; } - return $form; + // Check if the remove button is clicked. + if (end($triggering_element['#parents']) === 'remove_button') { + // When the list of added media is empty, return to the media library and + // shift focus back to the first tabbable element (which should be the + // source field). + if (empty($added_media)) { + $response->addCommand(new ReplaceCommand('#media-library-add-form-wrapper', $this->buildMediaLibraryUi($form_state))); + $response->addCommand(new InvokeCommand('#media-library-add-form-wrapper :tabbable', 'focus')); + } + // When there are still more items, update the form and shift the focus to + // the next media item. If the last list item is removed, shift focus to + // the previous item. + else { + $response->addCommand(new ReplaceCommand("#$wrapper_id", $form)); + + // Find the delta of the next media item. If there is no item with a + // bigger delta, we automatically use the delta of the previous item and + // shift the focus there. + $removed_delta = array_slice($triggering_element['#array_parents'], -2, 1)[0]; + $delta_to_focus = 0; + foreach ($added_media as $delta => $media) { + $delta_to_focus = $delta; + if ($delta > $removed_delta) { + break; + } + } + $response->addCommand(new InvokeCommand(".media-library-add-form__media[data-media-library-added-delta=$delta_to_focus]", 'focus')); + } + } + // Update the form and shift focus to the added media items. + else { + $response->addCommand(new ReplaceCommand("#$wrapper_id", $form)); + $response->addCommand(new InvokeCommand('.media-library-add-form__added-media', 'focus')); + } + + return $response; } /** @@ -420,6 +544,22 @@ public function updateWidget(array &$form, FormStateInterface $form_state) { return $media->id(); }, $added_media); + $response = new AjaxResponse(); + $response->addCommand(new UpdateSelectionCommand($media_ids)); + $response->addCommand(new ReplaceCommand('#media-library-add-form-wrapper', $this->buildMediaLibraryUi($form_state))); + return $response; + } + + /** + * Build the render array of the media library UI. + * + * @param \Drupal\Core\Form\FormStateInterface $form_state + * The current form state. + * + * @return array + * The render array for the media library. + */ + protected function buildMediaLibraryUi(FormStateInterface $form_state) { // Get the render array for the media library. The media library state might // contain the 'media_library_content' when it has been opened from a // vertical tab. We need to remove that to make sure the render array @@ -429,12 +569,7 @@ public function updateWidget(array &$form, FormStateInterface $form_state) { $state = $this->getMediaLibraryState($form_state); $state->remove('media_library_content'); $state->set('_media_library_form_rebuild', TRUE); - $library_ui = $this->libraryUiBuilder->buildUi($state); - - $response = new AjaxResponse(); - $response->addCommand(new UpdateSelectionCommand($media_ids)); - $response->addCommand(new ReplaceCommand('#media-library-add-form-wrapper', $library_ui)); - return $response; + return $this->libraryUiBuilder->buildUi($state); } /** diff --git a/core/modules/media_library/src/Form/FileUploadForm.php b/core/modules/media_library/src/Form/FileUploadForm.php index 8614a2e2d8ec..283ae74e977e 100644 --- a/core/modules/media_library/src/Form/FileUploadForm.php +++ b/core/modules/media_library/src/Form/FileUploadForm.php @@ -122,8 +122,16 @@ protected function buildInputElement(array $form, FormStateInterface $form_state $slots = $state->getAvailableSlots(); + // Add a container to group the input elements for styling purposes. + $form['container'] = [ + '#type' => 'container', + '#attributes' => [ + 'class' => ['media-library-add-form__input-wrapper'], + ], + ]; + $process = (array) $this->elementInfo->getInfoProperty('managed_file', '#process', []); - $form['upload'] = [ + $form['container']['upload'] = [ '#type' => 'managed_file', '#title' => $this->formatPlural($slots, 'Add file', 'Add files'), // @todo Move validation in https://www.drupal.org/node/2988215 @@ -136,7 +144,7 @@ protected function buildInputElement(array $form, FormStateInterface $form_state $file_upload_help = [ '#theme' => 'file_upload_help', - '#upload_validators' => $form['upload']['#upload_validators'], + '#upload_validators' => $form['container']['upload']['#upload_validators'], '#cardinality' => $slots, ]; @@ -145,7 +153,7 @@ protected function buildInputElement(array $form, FormStateInterface $form_state // upload help in the same way, so any theming improvements made to file // fields would also be applied to this upload field. // @see \Drupal\file\Plugin\Field\FieldWidget\FileWidget::formElement() - $form['upload']['#description'] = $this->renderer->renderPlain($file_upload_help); + $form['container']['upload']['#description'] = $this->renderer->renderPlain($file_upload_help); return $form; } diff --git a/core/modules/media_library/src/Form/OEmbedForm.php b/core/modules/media_library/src/Form/OEmbedForm.php index 72b2b1c54746..4af94334c03a 100644 --- a/core/modules/media_library/src/Form/OEmbedForm.php +++ b/core/modules/media_library/src/Form/OEmbedForm.php @@ -91,10 +91,11 @@ protected function buildInputElement(array $form, FormStateInterface $form_state $media_type = $this->getMediaType($form_state); $providers = $media_type->getSource()->getProviders(); + // Add a container to group the input elements for styling purposes. $form['container'] = [ '#type' => 'container', '#attributes' => [ - 'class' => ['media-library-add-form-oembed-wrapper'], + 'class' => ['media-library-add-form__input-wrapper'], ], ]; diff --git a/core/modules/media_library/tests/src/FunctionalJavascript/MediaLibraryTest.php b/core/modules/media_library/tests/src/FunctionalJavascript/MediaLibraryTest.php index 16db27bfd8e2..cbba55dc81e4 100644 --- a/core/modules/media_library/tests/src/FunctionalJavascript/MediaLibraryTest.php +++ b/core/modules/media_library/tests/src/FunctionalJavascript/MediaLibraryTest.php @@ -597,6 +597,7 @@ public function testWidgetAnonymous() { public function testWidgetUpload() { $assert_session = $this->assertSession(); $page = $this->getSession()->getPage(); + $driver = $this->getSession()->getDriver(); foreach ($this->getTestFiles('image') as $image) { $extension = pathinfo($image->filename, PATHINFO_EXTENSION); @@ -735,11 +736,6 @@ public function testWidgetUpload() { // Select a media item. $page->find('css', '.media-library-view .js-click-to-select-checkbox input')->click(); $assert_session->pageTextContains('1 item selected'); - - // Multiple uploads should be allowed. - // @todo Add test when https://github.com/minkphp/Mink/issues/358 is closed - $this->assertTrue($assert_session->fieldExists('Add files')->hasAttribute('multiple')); - $page->attachFileToField('Add files', $this->container->get('file_system')->realpath($png_image->uri)); $assert_session->assertWaitOnAjaxRequest(); $page->fillField('Name', 'Unlimited Cardinality Image'); @@ -822,6 +818,87 @@ public function testWidgetUpload() { $assert_session->assertWaitOnAjaxRequest(); $assert_session->pageTextNotContains('Add or select media'); $assert_session->pageTextContains($jpg_image->filename); + + // Assert removing an uploaded media item before save works as expected. + $assert_session->elementExists('css', '.media-library-open-button[name^="field_unlimited_media"]')->click(); + $assert_session->assertWaitOnAjaxRequest(); + $assert_session->pageTextContains('Add or select media'); + $page->clickLink('Type Three'); + $assert_session->assertWaitOnAjaxRequest(); + $page->attachFileToField('Add files', $this->container->get('file_system')->realpath($png_image->uri)); + $assert_session->assertWaitOnAjaxRequest(); + // Assert the focus is shifted to the added media items. + $this->assertJsCondition('jQuery(".media-library-add-form__added-media").is(":focus")'); + // Assert the media item fields are shown and the vertical tabs are no + // longer shown. + $assert_session->elementExists('css', '.media-library-add-form__fields'); + $assert_session->elementNotExists('css', '.media-library-menu'); + // Press the 'Remove button' and assert the user is sent back to the media + // library. + $assert_session->elementExists('css', '.media-library-add-form__remove-button')->click(); + $assert_session->assertWaitOnAjaxRequest(); + // Assert the remove message is shown. + $assert_session->pageTextContains("The media item $png_image->filename has been removed."); + // Assert the focus is shifted to the first tabbable element of the add + // form, which should be the source field. + $this->assertJsCondition('jQuery("#media-library-add-form-wrapper :tabbable").is(":focus")'); + $assert_session->elementNotExists('css', '.media-library-add-form__fields'); + $assert_session->elementExists('css', '.media-library-menu'); + $page->find('css', '.ui-dialog-titlebar-close')->click(); + + // Assert uploading multiple files. + $assert_session->elementExists('css', '.media-library-open-button[name^="field_unlimited_media"]')->click(); + $assert_session->assertWaitOnAjaxRequest(); + $assert_session->pageTextContains('Add or select media'); + $page->clickLink('Type Three'); + $assert_session->assertWaitOnAjaxRequest(); + $this->assertTrue($assert_session->fieldExists('Add files')->hasAttribute('multiple')); + // Create a list of new files to upload. + $filenames = []; + $remote_paths = []; + foreach (range(1, 3) as $i) { + $path = $file_system->copy($png_image->uri); + $filenames[] = $file_system->basename($path); + $remote_paths[] = $driver->uploadFileAndGetRemoteFilePath($file_system->realpath($path)); + } + $page->findField('Add files')->setValue(implode("\n", $remote_paths)); + $assert_session->assertWaitOnAjaxRequest(); + // Assert the media item fields are shown and the vertical tabs are no + // longer shown. + $assert_session->elementExists('css', '.media-library-add-form__fields'); + $assert_session->elementNotExists('css', '.media-library-menu'); + // Assert all files have been added. + $assert_session->fieldValueEquals('media[0][fields][name][0][value]', $filenames[0]); + $assert_session->fieldValueEquals('media[1][fields][name][0][value]', $filenames[1]); + $assert_session->fieldValueEquals('media[2][fields][name][0][value]', $filenames[2]); + // Set alt texts for items 1 and 2, leave the alt text empty for item 3 to + // assert the field validation does not stop users from removing items. + $page->fillField('media[0][fields][field_media_test_image][0][alt]', $filenames[0]); + $page->fillField('media[1][fields][field_media_test_image][0][alt]', $filenames[1]); + // Remove the second file and assert the focus is shifted to the container + // of the next media item and field values are still correct. + $page->pressButton('media-1-remove-button'); + $this->assertJsCondition('jQuery(".media-library-add-form__media[data-media-library-added-delta=2]").is(":focus")'); + $assert_session->pageTextContains('The media item ' . $filenames[1] . ' has been removed.'); + // The second media item should be removed (this has the delta 1 since we + // start counting from 0). + $assert_session->elementNotExists('css', '.media-library-add-form__media[data-media-library-added-delta=1]'); + $media_item_one = $assert_session->elementExists('css', '.media-library-add-form__media[data-media-library-added-delta=0]'); + $assert_session->fieldValueEquals('Name', $filenames[0], $media_item_one); + $assert_session->fieldValueEquals('Alternative text', $filenames[0], $media_item_one); + $media_item_three = $assert_session->elementExists('css', '.media-library-add-form__media[data-media-library-added-delta=2]'); + $assert_session->fieldValueEquals('Name', $filenames[2], $media_item_three); + $assert_session->fieldValueEquals('Alternative text', '', $media_item_three); + // Remove the last file and assert the focus is shifted to the container + // of the first media item and field values are still correct. + $page->pressButton('media-2-remove-button'); + $this->assertJsCondition('jQuery(".media-library-add-form__media[data-media-library-added-delta=0]").is(":focus")'); + $assert_session->pageTextContains('The media item ' . $filenames[2] . ' has been removed.'); + $assert_session->elementNotExists('css', '.media-library-add-form__media[data-media-library-added-delta=1]'); + $assert_session->elementNotExists('css', '.media-library-add-form__media[data-media-library-added-delta=2]'); + $media_item_one = $assert_session->elementExists('css', '.media-library-add-form__media[data-media-library-added-delta=0]'); + $assert_session->fieldValueEquals('Name', $filenames[0], $media_item_one); + $assert_session->fieldValueEquals('Alternative text', $filenames[0], $media_item_one); } /** @@ -921,6 +998,33 @@ public function testWidgetOEmbed() { $assert_session->assertWaitOnAjaxRequest(); $assert_session->pageTextNotContains('Add or select media'); $assert_session->pageTextContains('Custom video title'); + + // Assert removing an added oEmbed media item before save works as expected. + $assert_session->elementExists('css', '.media-library-open-button[name^="field_unlimited_media"]')->click(); + $assert_session->assertWaitOnAjaxRequest(); + $assert_session->pageTextContains('Add or select media'); + $page->clickLink('Type Five'); + $assert_session->assertWaitOnAjaxRequest(); + $page->fillField('Add Type Five via URL', $video_url); + $page->pressButton('Add'); + $assert_session->assertWaitOnAjaxRequest(); + // Assert the focus is shifted to the added media items. + $this->assertJsCondition('jQuery(".media-library-add-form__added-media").is(":focus")'); + // Assert the media item fields are shown and the vertical tabs are no + // longer shown. + $assert_session->elementExists('css', '.media-library-add-form__fields'); + $assert_session->elementNotExists('css', '.media-library-menu'); + // Press the 'Remove button' and assert the user is sent back to the media + // library. + $assert_session->elementExists('css', '.media-library-add-form__remove-button')->click(); + $assert_session->assertWaitOnAjaxRequest(); + // Assert the remove message is shown. + $assert_session->pageTextContains("The media item $video_title has been removed."); + // Assert the focus is shifted to the first tabbable element of the add + // form, which should be the source field. + $this->assertJsCondition('jQuery("#media-library-add-form-wrapper :tabbable").is(":focus")'); + $assert_session->elementNotExists('css', '.media-library-add-form__fields'); + $assert_session->elementExists('css', '.media-library-menu'); } } diff --git a/core/modules/media_library/tests/src/Kernel/MediaLibraryAddFormTest.php b/core/modules/media_library/tests/src/Kernel/MediaLibraryAddFormTest.php index 0d180bb8801d..833ede9a6546 100644 --- a/core/modules/media_library/tests/src/Kernel/MediaLibraryAddFormTest.php +++ b/core/modules/media_library/tests/src/Kernel/MediaLibraryAddFormTest.php @@ -87,7 +87,7 @@ public function testMediaTypeAddForm() { ])); // Assert the media library UI only contains the add form for the image // media type. - $this->assertSame('managed_file', $this->buildLibraryUi('image')['content']['form']['upload']['#type']); + $this->assertSame('managed_file', $this->buildLibraryUi('image')['content']['form']['container']['upload']['#type']); $this->assertEmpty($this->buildLibraryUi('remote_video')['content']['form']); // Create a user that has access to create both media types. @@ -97,7 +97,7 @@ public function testMediaTypeAddForm() { ])); // Assert the media library UI only contains the add form for both media // types. - $this->assertSame('managed_file', $this->buildLibraryUi('image')['content']['form']['upload']['#type']); + $this->assertSame('managed_file', $this->buildLibraryUi('image')['content']['form']['container']['upload']['#type']); $this->assertSame('url', $this->buildLibraryUi('remote_video')['content']['form']['container']['url']['#type']); } -- GitLab