diff --git a/core/modules/media_library/config/install/views.view.media_library.yml b/core/modules/media_library/config/install/views.view.media_library.yml index dd31c1169b..5c14762aef 100644 --- a/core/modules/media_library/config/install/views.view.media_library.yml +++ b/core/modules/media_library/config/install/views.view.media_library.yml @@ -672,7 +672,7 @@ display: table: views field: display_link display_id: widget - label: 'Show as grid' + label: 'Grid' plugin_id: display_link empty: true display_link_table: @@ -680,7 +680,7 @@ display: table: views field: display_link display_id: widget_table - label: 'Show as table' + label: 'Table' plugin_id: display_link empty: true css_class: 'media-library-view js-media-library-view media-library-view--widget' @@ -915,7 +915,7 @@ display: table: views field: display_link display_id: widget - label: 'Show as grid' + label: 'Grid' plugin_id: display_link empty: true display_link_table: @@ -923,7 +923,7 @@ display: table: views field: display_link display_id: widget_table - label: 'Show as table' + label: 'Table' plugin_id: display_link empty: true css_class: 'media-library-view js-media-library-view media-library-view--widget' diff --git a/core/modules/media_library/css/media_library.theme.css b/core/modules/media_library/css/media_library.theme.css index 6347ec5fad..9e651f8281 100644 --- a/core/modules/media_library/css/media_library.theme.css +++ b/core/modules/media_library/css/media_library.theme.css @@ -17,7 +17,7 @@ display: block; width: 600px; max-width: 20%; - margin: 0; + margin: 0; /* LTR */ padding: 0; border-bottom: 1px solid #ccc; background-color: #e6e5e1; @@ -58,9 +58,9 @@ .media-library-menu__link.active { z-index: 1; - margin-right: -1px; + margin-right: -1px; /* LTR */ color: #000; - border-right: 1px solid #fcfcfa; + border-right: 1px solid #fcfcfa; /* LTR */ border-bottom: 1px solid #b3b2ad; background-color: #fff; box-shadow: 0 5px 5px -5px hsla(0, 0%, 0%, 0.3); @@ -75,7 +75,7 @@ .media-library-content { width: 100%; padding: 1em; - border-left: 1px solid #b3b2ad; + border-left: 1px solid #b3b2ad; /* LTR */ outline: none; } [dir="rtl"] .media-library-content { @@ -152,6 +152,12 @@ margin: 0.75em 0; } +/* Override the table display of the visually hidden labels so they won't take + up space. */ +.media-library-item label { + display: inline-block; +} + /* Media library widget view styles. */ .media-library-wrapper .media-library-view { position: relative; @@ -160,22 +166,20 @@ justify-content: space-between; } -/* @todo Remove order and reorder the views header and filters via a template - when styles are moved to the seven theme in - https://www.drupal.org/project/drupal/issues/2980769 */ +/* @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; margin: 8px 0; - text-align: right; + text-align: right; /* LTR */ } [dir="rtl"] .media-library-wrapper .view-header { text-align: left; } -/* @todo Remove order and reorder the views header and filters via a template - when styles are moved to the seven theme in - https://www.drupal.org/project/drupal/issues/2980769 */ +/* @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; } @@ -183,64 +187,65 @@ .media-library-wrapper .media-library-view .button--primary { position: absolute; top: 0; - left: 0; + left: 0; /* LTR */ } [dir="rtl"] .media-library-wrapper .media-library-view .button--primary { right: 0; left: auto; } -/* @todo Remove order and reorder the views header and filters via a template - when styles are moved to the seven theme in - https://www.drupal.org/project/drupal/issues/2980769 */ +/* @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 template - when styles are moved to the seven theme in - https://www.drupal.org/project/drupal/issues/2980769 */ +/* @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; } .media-library-wrapper .views-display-link { margin: 0; - padding-left: 24px; + padding-left: 22px; /* LTR */ color: #333; line-height: 16px; } [dir="rtl"] .media-library-wrapper .views-display-link { - padding-right: 24px; + padding-right: 22px; padding-left: 0; } .media-library-wrapper .views-display-link-widget { margin-right: 15px; - background: url(../../../misc/icons/333333/grid.svg) 0 0 no-repeat; + background: url(../../../misc/icons/333333/grid.svg) 0 0 no-repeat; /* LTR */ } [dir="rtl"] .media-library-wrapper .views-display-link-widget { - background: url(../../../misc/icons/333333/grid.svg) right 0 no-repeat; + background-position: right 0; } .media-library-wrapper .views-display-link-widget_table { - background: url(../../../misc/icons/333333/table.svg) 0 0 no-repeat; + background: url(../../../misc/icons/333333/table.svg) 0 0 no-repeat; /* LTR */ } [dir="rtl"] .media-library-wrapper .views-display-link-widget_table { - background: url(../../../misc/icons/333333/table.svg) right 0 no-repeat; + background-position: right 0; } /* Media library item grid styles. */ .media-library-item--grid { justify-content: center; width: 180px; - margin: 2px 16px 16px 2px; + margin: 2px 16px 16px 2px; /* LTR */ transition: border-color 0.2s, color 0.2s, background 0.2s; vertical-align: top; border: 1px solid #dbdbdb; background: #fff; } +[dir="rtl"] .media-library-item--grid { + margin: 2px 2px 16px 16px; +} .media-library-item--grid .field--name-thumbnail { overflow: hidden; @@ -250,6 +255,7 @@ .media-library-item--grid .field--name-thumbnail img { height: 180px; + object-fit: contain; object-position: center center; } @@ -257,11 +263,16 @@ .media-library-item--grid.is-hover, .media-library-item--grid.checked, .media-library-item--grid.is-focus { - margin: 0 14px 14px 0; + margin: 0 14px 14px 0; /* LTR */ border-width: 3px; border-color: #40b6ff; border-radius: 3px; } +[dir="rtl"] .media-library-item--grid.is-hover, +[dir="rtl"] .media-library-item--grid.checked, +[dir="rtl"] .media-library-item--grid.is-focus { + margin: 0 0 14px 14px; +} .media-library-item--grid.checked { border-color: #0076c0; @@ -345,14 +356,14 @@ position: relative; } -.media-library-widget-empty-text { - margin-bottom: 0; -} - /* @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; + margin-left: 0; /* LTR */ +} +[dir="rtl"] .button.media-library-open-button { + margin-right: 0; + margin-left: 1em; } .media-library-widget__toggle-weight { @@ -362,7 +373,8 @@ } .media-library-selection { - margin-bottom: 1.5rem; + margin-top: 1em; + margin-bottom: 0.5em; } /* Media library widget weight field styles. */ @@ -375,6 +387,7 @@ .media-library-item__remove:hover, .media-library-item__remove:focus, .media-library-item__remove.button, +.media-library-item__remove.button:first-child, .media-library-item__remove.button:disabled, .media-library-item__remove.button:disabled:active, .media-library-item__remove.button:hover, @@ -436,7 +449,7 @@ justify-content: center; align-items: center; width: 220px; - margin-right: 20px; + margin-right: 20px; /* LTR */ background: #ebebeb; } [dir="rtl"] .media-library-add-form__preview { diff --git a/core/modules/media_library/media_library.module b/core/modules/media_library/media_library.module index 6701389c47..81dd4e0434 100644 --- a/core/modules/media_library/media_library.module +++ b/core/modules/media_library/media_library.module @@ -5,6 +5,7 @@ * Contains hook implementations for the media_library module. */ +use Drupal\Component\Utility\UrlHelper; use Drupal\Core\Access\AccessResult; use Drupal\Core\Entity\EntityInterface; use Drupal\Core\Entity\Entity\EntityFormDisplay; @@ -21,6 +22,7 @@ use Drupal\media\MediaTypeInterface; use Drupal\media_library\Form\FileUploadForm; use Drupal\media_library\Form\OEmbedForm; +use Drupal\media_library\MediaLibraryState; use Drupal\views\Form\ViewsForm; use Drupal\views\Plugin\views\cache\CachePluginBase; use Drupal\views\ViewExecutable; @@ -115,6 +117,20 @@ function media_library_link_alter(&$variables) { function media_library_views_post_render(ViewExecutable $view, &$output, CachePluginBase $cache) { if ($view->id() === 'media_library') { $output['#attached']['library'][] = 'media_library/view'; + if (strpos($view->current_display, 'widget') === 0) { + $query = MediaLibraryState::fromRequest($view->getRequest())->all(); + // If the current query contains any parameters we use to contextually + // filter the view, ensure they persist across AJAX rebuilds. + // The ajax_path is shared for all AJAX views on the page, but our query + // parameters are prefixed and should not interfere with any other views. + // @todo Rework or remove this in https://www.drupal.org/node/2983451 + if (!empty($query)) { + $ajax_path = &$output['#attached']['drupalSettings']['views']['ajax_path']; + $parsed_url = UrlHelper::parse($ajax_path); + $query = array_merge($query, $parsed_url['query']); + $ajax_path = $parsed_url['path'] . '?' . UrlHelper::buildQuery($query); + } + } } } diff --git a/core/modules/media_library/media_library.post_update.php b/core/modules/media_library/media_library.post_update.php index 7f134a1a6e..8f999331a4 100644 --- a/core/modules/media_library/media_library.post_update.php +++ b/core/modules/media_library/media_library.post_update.php @@ -166,7 +166,7 @@ function media_library_post_update_table_display() { 'table' => 'views', 'field' => 'display_link', 'display_id' => 'widget', - 'label' => 'Show as grid', + 'label' => 'Grid', 'plugin_id' => 'display_link', 'empty' => TRUE, ], @@ -175,7 +175,7 @@ function media_library_post_update_table_display() { 'table' => 'views', 'field' => 'display_link', 'display_id' => 'widget_table', - 'label' => 'Show as table', + 'label' => 'Table', 'plugin_id' => 'display_link', 'empty' => TRUE, ], diff --git a/core/modules/media_library/src/MediaLibraryUiBuilder.php b/core/modules/media_library/src/MediaLibraryUiBuilder.php index c44b555656..7cd3619967 100644 --- a/core/modules/media_library/src/MediaLibraryUiBuilder.php +++ b/core/modules/media_library/src/MediaLibraryUiBuilder.php @@ -298,6 +298,12 @@ protected function buildMediaLibraryView(MediaLibraryState $state) { $view_executable = $this->viewsExecutableFactory->get($view); $display_id = 'widget'; + // Make sure the state parameters are set in the request so the view can + // pass the parameters along in the pager, filters etc. + $view_request = $view_executable->getRequest(); + $view_request->query->add($state->all()); + $view_executable->setRequest($view_request); + $args = [$state->getSelectedTypeId()]; $view_executable->setDisplay($display_id); diff --git a/core/modules/media_library/src/Plugin/views/field/MediaLibrarySelectForm.php b/core/modules/media_library/src/Plugin/views/field/MediaLibrarySelectForm.php index 5660e43f4a..ab2fe35bae 100644 --- a/core/modules/media_library/src/Plugin/views/field/MediaLibrarySelectForm.php +++ b/core/modules/media_library/src/Plugin/views/field/MediaLibrarySelectForm.php @@ -53,6 +53,9 @@ public function viewsForm(array &$form, FormStateInterface $form_state) { 'class' => ['media-library-views-form', 'js-media-library-views-form'], ]; + // Add the view to the form state. + $form_state->set('view', $this->view); + // Render checkboxes for all rows. $form[$this->options['id']]['#tree'] = TRUE; foreach ($this->view->result as $row_index => $row) { @@ -83,7 +86,7 @@ public function viewsForm(array &$form, FormStateInterface $form_state) { // not the form action. This causes bugs when this form is rendered from an // AJAX path like /views/ajax, which cannot process AJAX form submits. $url = parse_url($form['#action'], PHP_URL_PATH); - $query = \Drupal::request()->query->all(); + $query = $this->view->getRequest()->query->all(); $query[FormBuilderInterface::AJAX_FORM_REQUEST] = TRUE; $form['actions']['submit']['#ajax'] = [ 'url' => Url::fromUserInput($url), @@ -120,7 +123,7 @@ public static function updateWidget(array &$form, FormStateInterface $form_state $ids = implode(',', $selected); - $opener_id = MediaLibraryState::fromRequest(\Drupal::request())->getOpenerId(); + $opener_id = MediaLibraryState::fromRequest($form_state->get('view')->getRequest())->getOpenerId(); if ($field_id = MediaLibraryWidget::getOpenerFieldId($opener_id)) { $response ->addCommand(new InvokeCommand("[data-media-library-widget-value=\"$field_id\"]", 'val', [$ids])) diff --git a/core/modules/media_library/tests/src/FunctionalJavascript/MediaLibraryTest.php b/core/modules/media_library/tests/src/FunctionalJavascript/MediaLibraryTest.php index f0a9b289a8..eded7c347b 100644 --- a/core/modules/media_library/tests/src/FunctionalJavascript/MediaLibraryTest.php +++ b/core/modules/media_library/tests/src/FunctionalJavascript/MediaLibraryTest.php @@ -278,6 +278,22 @@ public function testWidget() { $assert_session->pageTextNotContains('Turtle'); $page->find('css', '.ui-dialog-titlebar-close')->click(); + // Assert the exposed name filter of the view. + $assert_session->elementExists('css', '.media-library-open-button[href*="field_unlimited_media"]')->click(); + $assert_session->assertWaitOnAjaxRequest(); + $session = $this->getSession(); + $session->getPage()->fillField('Name', 'Dog'); + $session->getPage()->pressButton('Apply Filters'); + $assert_session->assertWaitOnAjaxRequest(); + $assert_session->pageTextContains('Dog'); + $assert_session->pageTextNotContains('Bear'); + $session->getPage()->fillField('Name', ''); + $session->getPage()->pressButton('Apply Filters'); + $assert_session->assertWaitOnAjaxRequest(); + $assert_session->pageTextContains('Dog'); + $assert_session->pageTextContains('Bear'); + $page->find('css', '.ui-dialog-titlebar-close')->click(); + // Assert the media library contains header links to switch between the grid // and table display. $assert_session->elementExists('css', '.media-library-open-button[href*="field_unlimited_media"]')->click(); @@ -285,50 +301,56 @@ public function testWidget() { $assert_session->elementExists('css', '.media-library-view .media-library-item--grid'); $assert_session->elementNotExists('css', '.media-library-view .media-library-item--table'); $button_pane = $assert_session->elementExists('css', '.ui-dialog-buttonpane'); + // Assert the 'Apply filter' button is not moved to the button pane. $assert_session->buttonExists('Select media', $button_pane); $assert_session->buttonNotExists('Apply filters', $button_pane); - $page->hasLink('Show as grid'); - $page->clickLink('Show as table'); + $page->hasLink('Grid'); + $page->clickLink('Table'); + // Assert the display change is correctly announced for screen readers. $assert_session->waitForText('Loading table view.'); $assert_session->waitForText('Changed to table view.'); $assert_session->waitForElementVisible('css', '.media-library-view .media-library-item--table'); $assert_session->elementNotExists('css', '.media-library-view .media-library-item--grid'); + // Assert the 'Apply filter' button is not moved to the button pane. $assert_session->buttonExists('Select media', $button_pane); $assert_session->buttonNotExists('Apply filters', $button_pane); $assert_session->pageTextContains('Dog'); $assert_session->pageTextContains('Bear'); $assert_session->pageTextNotContains('Turtle'); + // Assert the exposed filters can be applied. + $page->fillField('Name', 'Dog'); + $page->pressButton('Apply Filters'); + $assert_session->assertWaitOnAjaxRequest(); $assert_session->pageTextContains('Dog'); - $assert_session->pageTextContains('Bear'); + $assert_session->pageTextNotContains('Bear'); $assert_session->pageTextNotContains('Turtle'); - $page->hasLink('Show as table'); - $page->clickLink('Show as grid'); + $page->checkField('Select Dog'); + $page->hasLink('Table'); + $page->clickLink('Grid'); + // Assert the display change is correctly announced for screen readers. $assert_session->waitForText('Loading grid view.'); $assert_session->waitForText('Changed to grid view.'); $assert_session->waitForElementVisible('css', '.media-library-view .media-library-item--grid'); $assert_session->elementNotExists('css', '.media-library-view .media-library-item--table'); + // Assert the exposed filters are persisted when changing display. + $this->assertSame('Dog', $page->findField('Name')->getValue()); $assert_session->pageTextContains('Dog'); - $assert_session->pageTextContains('Bear'); + $assert_session->pageTextNotContains('Bear'); $assert_session->pageTextNotContains('Turtle'); - $page->hasLink('Show as grid'); - $page->hasLink('Show as table'); - $page->find('css', '.ui-dialog-titlebar-close')->click(); - - // Assert the exposed name filter of the view. - $assert_session->elementExists('css', '.media-library-open-button[href*="field_unlimited_media"]')->click(); - $assert_session->assertWaitOnAjaxRequest(); - $session = $this->getSession(); - $session->getPage()->fillField('Name', 'Dog'); - $session->getPage()->pressButton('Apply Filters'); + $page->hasLink('Grid'); + $page->hasLink('Table'); + // Select the item. + $assert_session->elementExists('css', '.ui-dialog-buttonpane')->pressButton('Select media'); $assert_session->assertWaitOnAjaxRequest(); + // Ensure that the selection completed successfully. + $assert_session->pageTextNotContains('Media library'); $assert_session->pageTextContains('Dog'); $assert_session->pageTextNotContains('Bear'); - $session->getPage()->fillField('Name', ''); - $session->getPage()->pressButton('Apply Filters'); + $assert_session->pageTextNotContains('Turtle'); + // Clear the selection. + $assert_session->elementAttributeContains('css', '.media-library-item__remove', 'aria-label', 'Remove Dog'); + $assert_session->elementExists('css', '.media-library-item__remove')->click(); $assert_session->assertWaitOnAjaxRequest(); - $assert_session->pageTextContains('Dog'); - $assert_session->pageTextContains('Bear'); - $page->find('css', '.ui-dialog-titlebar-close')->click(); // Assert the selection is persistent in the media library modal, and // the number of selected items is displayed correctly.